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 740bbbfbd41c713c018ca50bc1f1df3e564fbb4c
Author: Pepijn Noltes <[email protected]>
AuthorDate: Thu Jan 5 22:31:56 2023 +0100

    Add properties type support to C++ properties
---
 libs/utils/gtest/src/CxxPropertiesTestSuite.cc |  29 +++
 libs/utils/gtest/src/HashMapTestSuite.cc       |  28 +++
 libs/utils/gtest/src/PropertiesTestSuite.cc    |  22 ++
 libs/utils/include/celix/Properties.h          | 277 +++++++++++++++++++++----
 libs/utils/include/celix/Version.h             | 121 +++++++++++
 libs/utils/include/celix_long_hash_map.h       |  21 +-
 libs/utils/include/celix_properties.h          | 143 +++++++------
 libs/utils/include/celix_string_hash_map.h     |  26 ++-
 libs/utils/src/celix_hash_map.c                |  18 +-
 libs/utils/src/properties.c                    |  31 ++-
 libs/utils/src/version.c                       |  13 +-
 11 files changed, 603 insertions(+), 126 deletions(-)

diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc 
b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
index d5acac77..bc60adbc 100644
--- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
@@ -105,6 +105,35 @@ TEST_F(CxxPropertiesTestSuite, testWrap) {
     celix_properties_destroy(props);
 }
 
+TEST_F(CxxPropertiesTestSuite, getType) {
+    celix::Properties props{};
+
+    props.set("bool", true);
+    props.set("long1", 1l);
+    props.set("long2", (int)1); //should lead to long;
+    props.set("long3", (unsigned int)1); //should lead to long;
+    props.set("long4", (short)1); //should lead to long;
+    props.set("long5", (unsigned short)1); //should lead to long;
+    props.set("long6", (char)1); //should lead to long;
+    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
+
+    EXPECT_EQ(props.getType("bool"), celix::Properties::ValueType::Bool);
+    EXPECT_EQ(props.getType("long1"), celix::Properties::ValueType::Long);
+    EXPECT_EQ(props.getType("long2"), celix::Properties::ValueType::Long);
+    EXPECT_EQ(props.getType("long3"), celix::Properties::ValueType::Long);
+    EXPECT_EQ(props.getType("long4"), celix::Properties::ValueType::Long);
+    EXPECT_EQ(props.getType("long5"), celix::Properties::ValueType::Long);
+    EXPECT_EQ(props.getType("long6"), celix::Properties::ValueType::Long);
+    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);
+
+}
+
+
 #if __cplusplus >= 201703L //C++17 or higher
 TEST_F(CxxPropertiesTestSuite, testStringView) {
     constexpr std::string_view stringViewKey = "KEY1";
diff --git a/libs/utils/gtest/src/HashMapTestSuite.cc 
b/libs/utils/gtest/src/HashMapTestSuite.cc
index fa0e7f62..85a4944f 100644
--- a/libs/utils/gtest/src/HashMapTestSuite.cc
+++ b/libs/utils/gtest/src/HashMapTestSuite.cc
@@ -546,3 +546,31 @@ TEST_F(HashMapTestSuite, IterateWithRemoveTest) {
     EXPECT_TRUE(celix_longHashMapIterator_isEnd(&iter2));
     celix_longHashMap_destroy(lMap);
 }
+
+TEST_F(HashMapTestSuite, IterateEndTest) {
+    auto* sMap1 = createStringHashMap(0);
+    auto* sMap2 = createStringHashMap(6);
+    auto* lMap1 = createLongHashMap(0);
+    auto* lMap2 = createLongHashMap(6);
+
+    auto sIter1 = celix_stringHashMap_end(sMap1);
+    auto sIter2 = celix_stringHashMap_end(sMap2);
+    auto lIter1 = celix_longHashMap_end(lMap1);
+    auto lIter2 = celix_longHashMap_end(lMap2);
+
+    EXPECT_EQ(sIter1.index, 0);
+    EXPECT_EQ(sIter2.index, 6);
+    EXPECT_EQ(lIter1.index, 0);
+    EXPECT_EQ(lIter2.index, 6);
+    EXPECT_TRUE(celix_stringHashMapIterator_isEnd(&sIter1));
+    EXPECT_TRUE(celix_stringHashMapIterator_isEnd(&sIter2));
+    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);
+}
diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc 
b/libs/utils/gtest/src/PropertiesTestSuite.cc
index f0537f75..b9b6eb95 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -439,3 +439,25 @@ TEST_F(PropertiesTestSuite, getVersion) {
     celix_version_destroy(emptyVersion);
     celix_properties_destroy(properties);
 }
+
+TEST_F(PropertiesTestSuite, TestEndOfProperties) {
+    auto* props = celix_properties_create();
+    celix_properties_set(props, "key1", "value1");
+    celix_properties_set(props, "key2", "value2");
+
+    celix_properties_iterator_t endIter = celix_properties_end(props);
+    EXPECT_EQ(endIter.index, 2);
+    EXPECT_TRUE(celix_propertiesIterator_isEnd(&endIter));
+
+    celix_properties_destroy(props);
+}
+
+TEST_F(PropertiesTestSuite, TestEndOfEmptyProperties) {
+    auto* props = celix_properties_create();
+
+    celix_properties_iterator_t endIter = celix_properties_end(props);
+    EXPECT_EQ(endIter.index, 0);
+    EXPECT_TRUE(celix_propertiesIterator_isEnd(&endIter));
+
+    celix_properties_destroy(props);
+}
diff --git a/libs/utils/include/celix/Properties.h 
b/libs/utils/include/celix/Properties.h
index 682fe490..c75119a3 100644
--- a/libs/utils/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -26,6 +26,7 @@
 
 #include "celix_properties.h"
 #include "celix_utils.h"
+#include "celix/Version.h"
 
 namespace celix {
 
@@ -39,6 +40,11 @@ namespace celix {
             setFields();
         }
 
+        explicit PropertiesIterator(celix_properties_iterator_t _iter) {
+            iter = std::move(_iter);
+            setFields();
+        }
+
         PropertiesIterator& operator++() {
             next();
             return *this;
@@ -64,18 +70,13 @@ namespace celix {
             setFields();
         }
 
-        void moveToEnd() {
-            first = {};
-            second = {};
-            end = true;
-        }
-
         std::string first{};
         std::string second{};
     private:
         void setFields() {
             if (celix_propertiesIterator_isEnd(&iter)) {
-                moveToEnd();
+                first = {};
+                second = {};
             } else {
                 first = iter.entry.key;
                 second = iter.entry.value;
@@ -97,6 +98,19 @@ namespace celix {
     public:
         using const_iterator = PropertiesIterator;
 
+        /**
+         * @brief Enum representing the possible types of a property value.
+         */
+        enum class ValueType {
+            Unset,    /**< Property value is not set. */
+            String,   /**< Property value is a string. */
+            Long,     /**< Property value is a long integer. */
+            Double,   /**< Property value is a double. */
+            Bool,     /**< Property value is a boolean. */
+            Version   /**< Property value is a Celix version. */
+        };
+
+
         class ValueRef {
         public:
 #if __cplusplus >= 201703L //C++17 or higher
@@ -139,8 +153,6 @@ namespace celix {
                 }
             }
 
-            //TODO get typed value
-
             operator std::string() const {
                 auto *cstr = getValue();
                 return cstr == nullptr ? std::string{} : std::string{cstr};
@@ -240,9 +252,7 @@ namespace celix {
          * @brief end iterator
          */
         [[nodiscard]] const_iterator end() const noexcept {
-            auto iter = PropertiesIterator{cProps.get()};
-            iter.moveToEnd();
-            return iter;
+            return PropertiesIterator{celix_properties_end(cProps.get())};
         }
 
         /**
@@ -256,9 +266,7 @@ namespace celix {
          * @brief constant end iterator
          */
         [[nodiscard]] const_iterator cend() const noexcept {
-            auto iter = PropertiesIterator{cProps.get()};
-            iter.moveToEnd();
-            return iter;
+            return PropertiesIterator{celix_properties_end(cProps.get())};
         }
 
 #if __cplusplus >= 201703L //C++17 or higher
@@ -271,32 +279,85 @@ namespace celix {
         }
 
         /**
-         * @brief Get the value as long for a property key or return the 
defaultValue if the key does not exists.
+         * @brief Get the value of the property with key as a long.
+         *
+         * @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 cannot be converted
+         *                         to a long.
+         * @return The long value of the property if it exists and can be 
converted, or the default value otherwise.
          */
         [[nodiscard]] long getAsLong(std::string_view key, long defaultValue) 
const {
             return celix_properties_getAsLong(cProps.get(), key.data(), 
defaultValue);
         }
 
         /**
-         * @brief Get the value as double for a property key or return the 
defaultValue if the key does not exists.
+         * @brief Get the value of the property with key as a double.
+         *
+         * @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 cannot be converted
+         *                         to a double.
+         * @return The double value of the property if it exists and can be 
converted, or the default value otherwise.
          */
         [[nodiscard]] double getAsDouble(std::string_view key, double 
defaultValue) const {
             return celix_properties_getAsDouble(cProps.get(), key.data(), 
defaultValue);
         }
 
         /**
-         * @brief Get the value as bool for a property key or return the 
defaultValue if the key does not exists.
+         * @brief Get the value of the property with key as a boolean.
+         *
+         * @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 cannot be converted
+         *                         to a boolean.
+         * @return The boolean value of the property if it exists and can be 
converted, or the default value otherwise.
          */
         [[nodiscard]] bool getAsBool(std::string_view key, bool defaultValue) 
const {
             return celix_properties_getAsBool(cProps.get(), key.data(), 
defaultValue);
         }
 
-        //TODO getType
+        /**
+         * @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.
+         */
+         //TODO test
+        [[nodiscard]] celix::Version getVersion(std::string_view key, 
celix::Version defaultValue = {}) {
+            auto* cVersion = celix_properties_getVersion(cProps.get(), 
key.data(), nullptr);
+            if (cVersion) {
+                return celix::Version{
+                    celix_version_getMajor(cVersion),
+                    celix_version_getMinor(cVersion),
+                    celix_version_getMicro(cVersion),
+                    celix_version_getQualifier(cVersion)};
+            }
+            return defaultValue;
+        }
 
-        //TODO getAsVersion
+        /**
+         * @brief Get the type of the property with key.
+         *
+         * @param[in] key The key of the property to get the type for.
+         * @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());
+        }
 
         /**
-         * @brief Sets a T&& property. Will use (std::) to_string to convert 
the value to string.
+         * @brief Set the value of a property.
+         *
+         * @tparam T The type of the value to set. This can be one of: bool, 
std::string_view, a type that is
+         *           convertible to std::string_view, bool, long, double, 
celix::Version, or celix_version_t*.
+         * @param[im] key The key of the property to set.
+         * @param[in] value The value to set. If the type of the value is not 
one of the above types, it will be
+         *            converted to a string using std::to_string before being 
set.
          */
         template<typename T>
         void set(std::string_view key, T&& value) {
@@ -307,8 +368,17 @@ namespace celix {
             } 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>) {
+                celix_properties_setBool(cProps.get(), key.data(), value);
+            } else if constexpr (std::is_convertible_v<std::decay_t<T>, long>) 
{
+                celix_properties_setLong(cProps.get(), key.data(), value);
+            } else if constexpr (std::is_convertible_v<std::decay_t<T>, 
double>) {
+                celix_properties_setDouble(cProps.get(), key.data(), value);
+            } else if constexpr (std::is_same_v<std::decay_t<T>, 
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);
             } else {
-                //TODO spit up to use setLong, setBool, setDouble and 
setVersion
                 using namespace std;
                 celix_properties_set(cProps.get(), key.data(), 
to_string(value).c_str());
             }
@@ -323,68 +393,181 @@ namespace celix {
         }
 
         /**
-         * @brief Get the value as long for a property key or return the 
defaultValue if the key does not exists.
+         * @brief Get the value of the property with key as a long.
+         *
+         * @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 cannot be converted
+         *                         to a long.
+         * @return The long value of the property if it exists and can be 
converted, or the default value otherwise.
          */
         [[nodiscard]] long getAsLong(const std::string& key, long 
defaultValue) const {
             return celix_properties_getAsLong(cProps.get(), key.c_str(), 
defaultValue);
         }
 
         /**
-         * @brief Get the value as double for a property key or return the 
defaultValue if the key does not exists.
+         * @brief Get the value of the property with key as a double.
+         *
+         * @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 cannot be converted
+         *                         to a double.
+         * @return The double value of the property if it exists and can be 
converted, or the default value otherwise.
          */
         [[nodiscard]] double getAsDouble(const std::string &key, double 
defaultValue) const {
             return celix_properties_getAsDouble(cProps.get(), key.c_str(), 
defaultValue);
         }
 
         /**
-         * @brief Get the value as bool for a property key or return the 
defaultValue if the key does not exists.
+         * @brief Get the value of the property with key as a boolean.
+         *
+         * @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 cannot be converted
+         *                         to a boolean.
+         * @return The boolean value of the property if it exists and can be 
converted, or the default value otherwise.
          */
         [[nodiscard]] bool getAsBool(const std::string &key, bool 
defaultValue) const {
             return celix_properties_getAsBool(cProps.get(), key.c_str(), 
defaultValue);
         }
+        
+        /**
+         * @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.
+         */
+        [[nodiscard]] celix::Version getVersion(const std::string& key, 
celix::Version defaultValue = {}) {
+            auto* cVersion = celix_properties_getVersion(cProps.get(), 
key.data(), nullptr);
+            if (cVersion) {
+                return celix::Version{
+                    celix_version_getMajor(cVersion),
+                    celix_version_getMinor(cVersion),
+                    celix_version_getMicro(cVersion),
+                    celix_version_getQualifier(cVersion)};
+            }
+            return defaultValue;
+        }
 
         /**
-         * @brief Sets a property
+         * @brief Get the type of the property with key.
+         *
+         * @param[in] key The key of the property to get the type for.
+         * @return The type of the property with the given key, or 
ValueType::Unset if the property
+         *         does not exist.
+         */
+        [[nodiscard]] ValueType getType(const std::string& key) {
+            return getAndConvertType(cProps, key.data());
+        }
+
+        /**
+         * @brief Set the value of a property.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set the property to.
          */
         void set(const std::string& key, const std::string& value) {
-            celix_properties_set(cProps.get(), key.c_str(), value.c_str());
+            celix_properties_set(cProps.get(), key.data(), value.c_str());
         }
 
         /**
-         * @brief Sets a bool property
+         * @brief Set the value of a property to a boolean.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The boolean value to set the property to.
          */
         void set(const std::string& key, bool value) {
-            celix_properties_setBool(cProps.get(), key.c_str(), value);
+            celix_properties_setBool(cProps.get(), key.data(), value);
         }
 
         /**
-         * @brief Sets a const char* property
+         * @brief Set the value of a property.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set the property to.
          */
         void set(const std::string& key, const char* value) {
-            celix_properties_set(cProps.get(), key.c_str(), value);
+            celix_properties_set(cProps.get(), key.data(), value);
         }
 
         /**
-         * @brief Sets a T property. Will use (std::) to_string to convert the 
value to string.
+         * @brief Sets a property with a value of type T.
+         *
+         * This function will use the std::to_string function to convert the 
value of type T to a string,
+         * which will be used as the value for the property.
+         *
+         * @tparam T The type of the value to set.
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set for the property.
          */
         template<typename T>
         void set(const std::string& key, T&& value) {
             using namespace std;
-            celix_properties_set(cProps.get(), key.c_str(), 
to_string(value).c_str());
+            celix_properties_set(cProps.get(), key.data()(), 
to_string(value).c_str());
+        }
+
+        /**
+         * @brief Sets a bool property value for a given key.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set for the property.
+         */
+        void set(const std::string& key, bool value) {
+            celix_properties_setBool(cProps.get(), key.data(), value);
+        }
+
+        /**
+         * @brief Sets a long property value for a given key.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set for the property.
+         */
+        void set(const std::string& key, long value) {
+            celix_properties_setLong(cProps.get(), key.data(), value);
+        }
+
+        /**
+         * @brief Sets a double property value for a given key.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set for the property.
+         */
+        void set(const std::string& key, double value) {
+            celix_properties_setDouble(cProps.get(), key.data(), value);
+        }
+
+        /**
+         * @brief Sets a celix::Version property value for a given key.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set for the property.
+         */
+        void set(const std::string& key, const celix::Version& value) {
+            celix_properties_setVersion(cProps.get(), key.data(), 
value.getCVersion());
         }
 
-        //TODO set long, double, boolean and version
+        /**
+         * @brief Sets a celix_version_t* property value for a given key.
+         *
+         * @param[in] key The key of the property to set.
+         * @param[in] value The value to set for the property.
+         */
+        void set(const std::string& key, const celix_version_t* value) {
+            celix_properties_setVersion(cProps.get(), key.data(), value);
+        }
 #endif
 
         /**
-         * @brief Returns the nr of properties.
+         * @brief Returns the number of properties in the Properties object.
          */
         [[nodiscard]] std::size_t size() const {
             return celix_properties_size(cProps.get());
         }
 
         /**
-         * @brief Converts the properties a (new) std::string, std::string map.
+         * @brief Convert the properties a (new) std::string, std::string map.
          */
         [[nodiscard]] std::map<std::string, std::string> convertToMap() const {
             std::map<std::string, std::string> result{};
@@ -395,7 +578,7 @@ namespace celix {
         }
 
         /**
-         * @brief Converts the properties a (new) std::string, std::string 
unordered map.
+         * @brief Convert the properties a (new) std::string, std::string 
unordered map.
          */
         [[nodiscard]] std::unordered_map<std::string, std::string> 
convertToUnorderedMap() const {
             std::unordered_map<std::string, std::string> result{};
@@ -428,6 +611,26 @@ namespace celix {
     private:
         explicit Properties(celix_properties_t* props) : cProps{props, 
[](celix_properties_t*) { /*nop*/ }} {}
 
+        static celix::Properties::ValueType getAndConvertType(
+                const std::shared_ptr<celix_properties_t>& cProperties,
+                const char* key) {
+            auto cType = celix_properties_getType(cProperties.get(), key);
+            switch (cType) {
+                case CELIX_PROPERTIES_VALUE_TYPE_STRING:
+                    return ValueType::String;
+                case CELIX_PROPERTIES_VALUE_TYPE_LONG:
+                    return ValueType::Long;
+                case CELIX_PROPERTIES_VALUE_TYPE_DOUBLE:
+                    return ValueType::Double;
+                case CELIX_PROPERTIES_VALUE_TYPE_BOOL:
+                    return ValueType::Bool;
+                case CELIX_PROPERTIES_VALUE_TYPE_VERSION:
+                    return ValueType::Version;
+                default: /*unset*/
+                    return ValueType::Unset;
+            }
+        }
+
         std::shared_ptr<celix_properties_t> cProps;
     };
 
diff --git a/libs/utils/include/celix/Version.h 
b/libs/utils/include/celix/Version.h
new file mode 100644
index 00000000..ce70d8d4
--- /dev/null
+++ b/libs/utils/include/celix/Version.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "celix_version.h"
+
+namespace celix {
+
+    //TODO CxxVersionTestSuite
+    //TODO doxygen
+    class Version {
+    public:
+        Version() : 
cVersion{createVersion(celix_version_createEmptyVersion())} {}
+#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()))} {}
+#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()))} {}
+#endif
+
+
+        Version(Version&&) = default;
+
+        Version& operator=(Version&&) = 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) {
+            return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) 
== 0;
+        }
+
+        bool operator<(const Version& rhs) {
+            return celix_version_compareTo(cVersion.get(), rhs.cVersion.get()) 
< 0;
+        }
+
+        //TODO rest of the operators
+
+        /**
+         * @brief Warps a C Celix Version to a C++ Celix Version, but takes no 
ownership.
+         * Dealloction is still the responsibility of the caller.
+         */
+        static Version wrap(celix_version_t* v) {
+            return Version{v};
+        }
+
+        /**
+         * @brief Get the underlining C Celix Version object.
+         *
+         * @warning Try not the depend on the C API from a C++ bundle. If 
features are missing these should be added to
+         * the C++ API.
+         */
+        [[nodiscard]] celix_version_t * getCVersion() const {
+            return cVersion.get();
+        }
+
+        [[nodiscard]] int getMajor() const {
+            return celix_version_getMajor(cVersion.get());
+        }
+
+        [[nodiscard]] int getMinor() const {
+            return celix_version_getMinor(cVersion.get());
+        }
+
+        [[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;
+        }
+    private:
+        static std::shared_ptr<celix_version_t> createVersion(celix_version_t* 
cVersion) {
+            return std::shared_ptr<celix_version_t>{cVersion, 
[](celix_version_t *v) {
+                celix_version_destroy(v);
+            }};
+        }
+
+        /**
+         * @brief Create a wrap around a C Celix version without taking 
ownership.
+         */
+        explicit Version(celix_version_t* v) : cVersion{v, [](celix_version_t 
*){/*nop*/}} {}
+
+        std::shared_ptr<celix_version_t> cVersion;
+    };
+}
\ 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 50bf810d..40e23bff 100644
--- a/libs/utils/include/celix_long_hash_map.h
+++ b/libs/utils/include/celix_long_hash_map.h
@@ -261,24 +261,33 @@ bool celix_longHashMap_remove(celix_long_hash_map_t* map, 
long key);
 void celix_longHashMap_clear(celix_long_hash_map_t* map);
 
 /**
- * @brief Returns an iterator pointing to the first element in the map.
+ * @brief Get an iterator pointing to the first element in the map.
  *
- * @param map The map to get the iterator for.
+ * @param[in] map The map to get the iterator for.
  * @return An iterator pointing to the first element in the map.
  */
 celix_long_hash_map_iterator_t celix_longHashMap_begin(const 
celix_long_hash_map_t* map);
 
 /**
- * @brief Check if the iterator is the end of the hash map.
+ * @brief Get an iterator pointing to the element following the last element 
in the map.
  *
- * @note the end iterator should not be used to retrieve a key of value.
+ * @param[in] map The map to get the iterator for.
+ * @return An iterator pointing to the element following the last element in 
the map.
+ */
+celix_long_hash_map_iterator_t celix_longHashMap_end(const 
celix_long_hash_map_t* map);
+
+/**
+ *
+ * @brief Determine if the iterator points to the element following the last 
element in the map.
  *
- * @return true if the iterator is the end.
+ * @param[in] iter The iterator to check.
+ * @return true if the iterator points to the element following the last 
element in the map, false otherwise.
  */
 bool celix_longHashMapIterator_isEnd(const celix_long_hash_map_iterator_t* 
iter);
 
 /**
- * @brief Moves the provided iterator to the next entry in the hash map.
+ * @brief Advance the iterator to the next element in the map.
+ * @param[in] iter The iterator to advance.
  */
 void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter);
 
diff --git a/libs/utils/include/celix_properties.h 
b/libs/utils/include/celix_properties.h
index e484a436..7794661e 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -103,6 +103,7 @@ typedef struct celix_properties_iterator {
 
 /**
  * @brief Creates a new empty property set.
+ *
  * @return A new empty property set.
  */
 celix_properties_t* celix_properties_create(void);
@@ -110,14 +111,14 @@ celix_properties_t* celix_properties_create(void);
 /**
  * @brief Destroys a property set, freeing all associated resources.
  *
- * @param properties The property set to destroy. If properties is NULL, this 
function will do nothing.
+ * @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.
  *
- * @param filename The name of the file to load properties from.
+ * @param[in] filename The name of the file to load properties from.
  * @return A property set containing the properties from the file.
  * @retval NULL If an error occurred (e.g. file not found).
  */
@@ -127,7 +128,7 @@ celix_properties_t* celix_properties_load(const char 
*filename);
 /**
  * @brief Loads properties from a stream.
  *
- * @param stream The stream to load properties from.
+ * @param[in,out] stream The stream to load properties from.
  * @return A property set containing the properties from the stream.
  * @retval NULL If an error occurred (e.g. invalid format).
  */
@@ -136,7 +137,7 @@ celix_properties_t* celix_properties_loadWithStream(FILE 
*stream);
 /**
  * @brief Loads properties from a string.
  *
- * @param input The string to load properties from.
+ * @param[in] input The string to load properties from.
  * @return A property set containing the properties from the string.
  * @retval NULL If an error occurred (e.g. invalid format).
  */
@@ -145,9 +146,9 @@ celix_properties_t* celix_properties_loadFromString(const 
char *input);
 /**
  * @brief Stores properties to a file.
  *
- * @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.
+ * @param[in] properties The property set to store.
+ * @param[in] file The name of the file to store the properties to.
+ * @param[in] 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.
  */
@@ -156,8 +157,8 @@ celix_status_t celix_properties_store(celix_properties_t* 
properties, const char
 /**
  * @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.
+ * @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.
  */
@@ -166,63 +167,64 @@ celix_properties_entry_t celix_properties_getEntry(const 
celix_properties_t* pro
 /**
  * @brief Gets the value of a property.
  *
- * @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.
+ * @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.
  * @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);
 
 /**
  * @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.
+ *
+ * @param[in] properties The property set to search.
+ * @param[in] 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.
+ * @brief Set the value of a property.
  *
- *
- * @param properties The property set to modify.
- * @param key The key of the property to set.
- * @param value The value to set the property to.
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] value The value to set the property to.
  */
 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.
+ * @brief Set the value of a property without copying the key and value 
strings.
  *
- * @param properties The property set to modify.
- * @param key The key of the property to set. This string will be used 
directly, so it must not be freed or modified
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set. This string will be used 
directly, so it must not be freed or modified
  *            after calling this function.
- * @param value The value to set the property to. This string will be used 
directly, so it must not be freed or
+ * @param[in] 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);
 
 /**
- * @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.
+ * @brief Unset a property, removing it from the property set.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to unset.
  */
 void celix_properties_unset(celix_properties_t* properties, const char *key);
 
 /**
- * @brief Makes a copy of a property set.
+ * @brief Make a copy of a property set.
  *
- * @param properties The property set to copy.
+ * @param[in] 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);
 
 /**
- * @brief Gets the value of a property as a long integer.
+ * @brief Get the value of a property as a long integer.
  *
- * @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 long integer,
+ * @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, 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.
@@ -231,20 +233,20 @@ celix_properties_t* celix_properties_copy(const 
celix_properties_t* properties);
 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.
+ * @brief Set the value of a property to a long integer.
  *
- * @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.
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] value The long value to set the property to.
  */
 void celix_properties_setLong(celix_properties_t* properties, const char* key, 
long value);
 
 /**
- * @brief Gets the value of a property as a boolean.
+ * @brief Get the value of a property as a boolean.
  *
- * @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 boolean, or if the value
+ * @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, 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
@@ -253,29 +255,29 @@ void celix_properties_setLong(celix_properties_t* 
properties, const char* key, l
 bool celix_properties_getAsBool(const celix_properties_t* properties, const 
char* key, bool defaultValue);
 
 /**
- * @brief Sets the value of a property to a boolean.
+ * @brief Set the value of a property to a boolean.
  *
- * @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.
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] val The boolean value to set the property to.
  */
 void celix_properties_setBool(celix_properties_t* properties, const char* key, 
bool val);
 
 /**
- * @brief Sets the value of a property to a double.
+ * @brief Set the value of a property to a double.
  *
- * @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.
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] val The double value to set the property to.
  */
 void celix_properties_setDouble(celix_properties_t* properties, const char* 
key, double val);
 
 /**
- * @brief Gets the value of a property as a double.
+ * @brief Get the value of a property as a double.
  *
- * @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 double,
+ * @param[in] properties The property set to search.
+ * @param[in] key The key of the property to get.
+ * @param[in] defaultValue The value to return if the property is not set, 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
@@ -284,7 +286,7 @@ void celix_properties_setDouble(celix_properties_t* 
properties, const char* key,
 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.
+ * @brief Set the value of a property as a Celix version.
  *
  * This function will make a copy of the provided celix_version_t object and 
store it in the property set.
  *
@@ -295,7 +297,7 @@ double celix_properties_getAsDouble(const 
celix_properties_t* properties, const
 void celix_properties_setVersion(celix_properties_t* properties, const char* 
key, const celix_version_t* version);
 
 /**
- * @brief Sets the value of a property as a Celix version.
+ * @brief Set the value of a property as a Celix version.
  *
  * This function will store a reference to the provided celix_version_t object 
in the property set and takes
  * ownership of the provided version.
@@ -309,7 +311,7 @@ void 
celix_properties_setVersionWithoutCopy(celix_properties_t* properties, cons
 
 
 /**
- * @brief Gets the value of a property as a Celix version.
+ * @brief Get the value of a property as a Celix version.
  *
  * This function does not convert a string property value to a Celix version 
automatically.
  *
@@ -325,15 +327,15 @@ const celix_version_t* celix_properties_getVersion(
         const celix_version_t* defaultValue);
 
 /**
- * @brief Gets the number of properties in a property set.
+ * @brief Get the number of properties in a property set.
  *
- * @param properties The property set to get the size of.
+ * @param[in] 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);
 
 /**
- * @brief Constructs an iterator pointing to the first entry in the properties 
object.
+ * @brief Construct 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.
@@ -341,14 +343,25 @@ int celix_properties_size(const celix_properties_t* 
properties);
 celix_properties_iterator_t celix_properties_begin(const celix_properties_t* 
properties);
 
 /**
- * @brief Advances the iterator to the next entry.
+ * @brief Construct an iterator pointing to the past-the-end entry in the 
properties object.
+ *
+ * This iterator is used to mark the end of the properties object and is not 
associated with any element in the
+ * properties object.
+ *
+ * @param[in] properties The properties object to iterate over.
+ * @return The iterator pointing to the past-the-end entry in the properties 
object.
+ */
+celix_properties_iterator_t celix_properties_end(const celix_properties_t* 
properties);
+
+/**
+ * @brief Advance 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.
+ * @brief Determine whether the iterator is pointing to an end position.
  *
  * An iterator is at an end position if it has no more entries to visit.
  *
@@ -358,7 +371,7 @@ void 
celix_propertiesIterator_next(celix_properties_iterator_t* iter);
 bool celix_propertiesIterator_isEnd(const celix_properties_iterator_t* iter);
 
 /**
- * @brief Gets the property set being iterated over.
+ * @brief Get the property set being iterated over.
  *
  * @param[in] iter The iterator to get the property set from.
  * @return The property set being iterated over.
@@ -366,7 +379,7 @@ bool celix_propertiesIterator_isEnd(const 
celix_properties_iterator_t* iter);
 celix_properties_t* celix_propertiesIterator_properties(const 
celix_properties_iterator_t *iter);
 
 /**
- * @brief Determines whether two iterators are equal.
+ * @brief Determine whether two iterators are equal.
  *
  * @param[in] a The first iterator to compare.
  * @param[in] b The second iterator to compare.
@@ -375,14 +388,14 @@ celix_properties_t* 
celix_propertiesIterator_properties(const celix_properties_i
 bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, 
const celix_properties_iterator_t* b);
 
 /**
- * @brief Iterates over the entries in the specified celix_properties_t object.
+ * @brief Iterate 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.
+ * @param[in] map The properties object to iterate over.
+ * @param[in] iterName The name of the iterator variable to use in the loop.
  *
  * Example usage:
  * @code{.c}
diff --git a/libs/utils/include/celix_string_hash_map.h 
b/libs/utils/include/celix_string_hash_map.h
index 1e5fbe82..c3ae78bc 100644
--- a/libs/utils/include/celix_string_hash_map.h
+++ b/libs/utils/include/celix_string_hash_map.h
@@ -270,33 +270,41 @@ bool celix_stringHashMap_remove(celix_string_hash_map_t* 
map, const char* key);
 void celix_stringHashMap_clear(celix_string_hash_map_t* map);
 
 /**
- * @brief Returns an iterator pointing to the first element in the map.
+ * @brief Get an iterator pointing to the first element in the map.
  *
- * @param map The map to get the iterator for.
+ * @param[in] map The map to get the iterator for.
  * @return An iterator pointing to the first element in the map.
  */
 celix_string_hash_map_iterator_t celix_stringHashMap_begin(const 
celix_string_hash_map_t* map);
 
 /**
- * @brief Check if the iterator is the end of the hash map.
+ * @brief Get an iterator pointing to the element following the last element 
in the map.
  *
- * @note the end iterator should not be used to retrieve a key of value.
+ * @param[in] map The map to get the iterator for.
+ * @return An iterator pointing to the element following the last element in 
the map.
+ */
+celix_string_hash_map_iterator_t celix_stringHashMap_end(const 
celix_string_hash_map_t* map);
+
+/**
  *
- * @return true if the iterator is the end.
+ * @brief Determine if the iterator points to the element following the last 
element in the map.
+ *
+ * @param[in] iter The iterator to check.
+ * @return true if the iterator points to the element following the last 
element in the map, false otherwise.
  */
 bool celix_stringHashMapIterator_isEnd(const celix_string_hash_map_iterator_t* 
iter);
 
 /**
- * @brief Moves the provided iterator to the next entry in the hash map.
+ * @brief Advance the iterator to the next element in the map.
+ * @param[in] iter The iterator to advance.
  */
 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
+ * @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);
@@ -313,7 +321,7 @@ void 
celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter);
  * @brief Remove the hash map entry for the provided iterator and updates the 
iterator to the next hash map entry
  *
  * Small example of how to use the celix_stringHashMapIterator_remove function:
- * @code
+ * @code{.c}
  * //remove all even entries from hash map
  * celix_string_hash_map_t* map = ...
  * celix_string_hash_map_iterator_t iter = celix_stringHashMap_begin(map);
diff --git a/libs/utils/src/celix_hash_map.c b/libs/utils/src/celix_hash_map.c
index 1ef9b75e..32bd7da3 100644
--- a/libs/utils/src/celix_hash_map.c
+++ b/libs/utils/src/celix_hash_map.c
@@ -588,6 +588,22 @@ celix_long_hash_map_iterator_t 
celix_longHashMap_begin(const celix_long_hash_map
     return iter;
 }
 
+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;
+    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;
+    return iter;
+}
+
 bool celix_stringHashMapIterator_isEnd(const celix_string_hash_map_iterator_t* 
iter) {
     return iter->_internal[1] == NULL; //check if entry is NULL
 }
@@ -600,9 +616,9 @@ 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) {
         iter->_internal[1] = entry;
-        iter->index += 1;
         iter->key = entry->key.strKey;
         iter->value = entry->value;
     } else {
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index 7be3e713..fbd60715 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -342,6 +342,15 @@ celix_properties_t* celix_properties_create(void) {
 
 void celix_properties_destroy(celix_properties_t *props) {
     if (props != NULL) {
+        //TODO measure print nr of entries and total size of the string keys 
and values
+//        fprintf(stdout, "Properties size; %d", celix_properties_size(props));
+//        size_t size = 0;
+//        CELIX_PROPERTIES_ITERATE(props, iter) {
+//            size += strlen(iter.entry.key) + 1;
+//            size += strlen(iter.entry.value) + 1;
+//        }
+//        fprintf(stdout, "Properties string size: %zu", size);
+
         celix_stringHashMap_destroy(props->map);
         free(props);
     }
@@ -448,12 +457,14 @@ static void parseLine(const char* line, 
celix_properties_t *props) {
         //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_setWithoutCopy(props, key, value);
+        celix_properties_set(props, key, value);
     } else {
-        free(key);
-        free(value);
+//        free(key);
+//        free(value);
     }
-
+    free(key);
+    free(value);
 
 }
 
@@ -786,6 +797,18 @@ celix_properties_iterator_t celix_properties_begin(const 
celix_properties_t* pro
     return iter;
 }
 
+celix_properties_iterator_t celix_properties_end(const celix_properties_t* 
properties) {
+    celix_properties_iterator_internal_t internalIter;
+    internalIter.mapIter = celix_stringHashMap_end(properties->map);
+    internalIter.props = properties;
+
+    celix_properties_iterator_t iter;
+    memset(&iter, 0, sizeof(iter));
+    iter.index = (int)internalIter.mapIter.index; //TODO make inter.index 
size_t
+    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));
diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c
index 3d0ee1ab..72a37942 100644
--- a/libs/utils/src/version.c
+++ b/libs/utils/src/version.c
@@ -27,6 +27,8 @@
 #include "celix_errno.h"
 #include "version_private.h"
 
+static const char* const CELIX_VERSION_EMPTY_QUALIFIER = "";
+
 celix_status_t version_createVersion(int major, int minor, int micro, const 
char * qualifier, version_pt *version) {
     *version = celix_version_createVersion(major, minor, micro, qualifier);
     return *version == NULL ? CELIX_ILLEGAL_ARGUMENT : CELIX_SUCCESS;
@@ -107,7 +109,8 @@ celix_version_t* celix_version_create(int major, int minor, 
int micro, const cha
     if (qualifier == NULL) {
         qualifier = "";
     }
-    for (int i = 0; i < strlen(qualifier); i++) {
+    size_t qualifierLen = strlen(qualifier);
+    for (int i = 0; i < qualifierLen; i++) {
         char ch = qualifier[i];
         if (('A' <= ch) && (ch <= 'Z')) {
             continue;
@@ -125,12 +128,12 @@ celix_version_t* celix_version_create(int major, int 
minor, int micro, const cha
         return NULL;
     }
 
-    celix_version_t* version = calloc(1, sizeof(*version));
+    celix_version_t* version = malloc(sizeof(*version));
 
     version->major = major;
     version->minor = minor;
     version->micro = micro;
-    version->qualifier = celix_utils_strdup(qualifier);
+    version->qualifier = qualifierLen == 0 ? 
(char*)CELIX_VERSION_EMPTY_QUALIFIER : celix_utils_strdup(qualifier);
 
 
     return version;
@@ -138,7 +141,9 @@ celix_version_t* celix_version_create(int major, int minor, 
int micro, const cha
 
 void celix_version_destroy(celix_version_t* version) {
     if (version != NULL) {
-        free(version->qualifier);
+        if (version->qualifier != CELIX_VERSION_EMPTY_QUALIFIER) {
+            free(version->qualifier);
+        }
         free(version);
     }
 }

Reply via email to