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