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