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 1ff603ba0702464c928bd9c22475bf834628f85b Author: Pepijn Noltes <[email protected]> AuthorDate: Sun Jan 1 20:13:06 2023 +0100 Refactor celix properties to be based on celix_string_hash_map. --- .../pubsub_discovery/src/pubsub_discovery_impl.c | 2 +- .../src/endpoint_descriptor_writer.c | 13 +- .../src/remote_service_admin_dfi.c | 15 +- .../rsa_shm/src/rsa_shm_impl.c | 5 +- .../topology_manager/src/topology_manager.c | 3 +- .../topology_manager/tms_tst/tms_tests.cpp | 9 +- libs/framework/src/service_reference.c | 11 +- libs/utils/CMakeLists.txt | 10 +- libs/utils/gtest/CMakeLists.txt | 5 + .../resources}/properties.txt | 0 libs/utils/gtest/src/CxxPropertiesTestSuite.cc | 16 + .../src/PropertiesTestSuite.cc} | 171 +++---- libs/utils/include/celix/Properties.h | 19 +- libs/utils/include/celix_long_hash_map.h | 4 + libs/utils/include/celix_properties.h | 243 +++++++++- libs/utils/include/celix_string_hash_map.h | 5 + libs/utils/include_deprecated/properties.h | 7 +- libs/utils/src/properties.c | 523 +++++++++++++++++++++ libs/utils/src/utils.c | 2 +- 19 files changed, 884 insertions(+), 179 deletions(-) diff --git a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c index c6d1aa95..4815bd92 100644 --- a/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c +++ b/bundles/pubsub/pubsub_discovery/src/pubsub_discovery_impl.c @@ -558,7 +558,7 @@ static char* pubsub_discovery_createJsonEndpoint(const celix_properties_t *props json_t *jsEndpoint = json_object(); const char* propKey = NULL; - PROPERTIES_FOR_EACH((celix_properties_t*)props, propKey) { + CELIX_PROPERTIES_FOR_EACH(props, propKey) { const char* val = celix_properties_get(props, propKey, NULL); json_object_set_new(jsEndpoint, propKey, json_string(val)); } diff --git a/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c b/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c index d2e2421f..66580b5a 100644 --- a/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c +++ b/bundles/remote_services/discovery_common/src/endpoint_descriptor_writer.c @@ -139,15 +139,11 @@ static celix_status_t endpointDescriptorWriter_writeEndpoint(endpoint_descriptor } else { xmlTextWriterStartElement(writer->writer, ENDPOINT_DESCRIPTION); - hash_map_iterator_pt iter = hashMapIterator_create(endpoint->properties); - while (hashMapIterator_hasNext(iter)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(iter); - - void* propertyName = hashMapEntry_getKey(entry); - const xmlChar* propertyValue = (const xmlChar*) hashMapEntry_getValue(entry); - + const char* propertyName; + CELIX_PROPERTIES_FOR_EACH(endpoint->properties, propertyName) { + const xmlChar* propertyValue = (const xmlChar*) celix_properties_get(endpoint->properties, propertyName, ""); xmlTextWriterStartElement(writer->writer, PROPERTY); - xmlTextWriterWriteAttribute(writer->writer, NAME, propertyName); + xmlTextWriterWriteAttribute(writer->writer, NAME, (const xmlChar*)propertyName); if (strcmp(OSGI_FRAMEWORK_OBJECTCLASS, (char*) propertyName) == 0) { // objectClass *must* be represented as array of string values... @@ -162,7 +158,6 @@ static celix_status_t endpointDescriptorWriter_writeEndpoint(endpoint_descriptor xmlTextWriterEndElement(writer->writer); } - hashMapIterator_destroy(iter); xmlTextWriterEndElement(writer->writer); } diff --git a/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c b/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c index 31bddb24..656b3773 100644 --- a/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c +++ b/bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c @@ -723,10 +723,7 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic } } - hash_map_entry_pt entry = hashMap_getEntry(endpointProperties, (void *) OSGI_FRAMEWORK_SERVICE_ID); - - char* key = hashMapEntry_getKey(entry); - char *serviceId = (char *) hashMap_remove(endpointProperties, (void *) OSGI_FRAMEWORK_SERVICE_ID); + const char* serviceId = celix_properties_get(endpointProperties, CELIX_FRAMEWORK_SERVICE_ID, "-1"); const char *uuid = NULL; char buf[512]; @@ -750,12 +747,10 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic celix_properties_set(endpointProperties, RSA_DFI_ENDPOINT_URL, url); if (props != NULL) { - hash_map_iterator_pt propIter = hashMapIterator_create(props); - while (hashMapIterator_hasNext(propIter)) { - hash_map_entry_pt entry = hashMapIterator_nextEntry(propIter); - celix_properties_set(endpointProperties, (char*)hashMapEntry_getKey(entry), (char*)hashMapEntry_getValue(entry)); + const char* key; + CELIX_PROPERTIES_FOR_EACH(props, key) { + celix_properties_set(endpointProperties, key, celix_properties_get(props, key, "")); } - hashMapIterator_destroy(propIter); } *endpoint = calloc(1, sizeof(**endpoint)); @@ -769,8 +764,6 @@ static celix_status_t remoteServiceAdmin_createEndpointDescription(remote_servic (*endpoint)->properties = endpointProperties; } - free(key); - free(serviceId); free(keys); return status; diff --git a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c index b72cb95d..2d8c2bc9 100755 --- a/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c +++ b/bundles/remote_services/remote_service_admin_shm_v2/rsa_shm/src/rsa_shm_impl.c @@ -230,12 +230,12 @@ static void rsaShm_overlayProperties(celix_properties_t *additionalProperties, c * A property key in the additional properties map must therefore override any case variant property key in the properties of the specified Service Reference.*/ const char *additionalPropKey = NULL; const char *servicePropKey = NULL; - PROPERTIES_FOR_EACH(additionalProperties, additionalPropKey) { + CELIX_PROPERTIES_FOR_EACH(additionalProperties, additionalPropKey) { if (strcmp(additionalPropKey,(char*) OSGI_FRAMEWORK_OBJECTCLASS) != 0 && strcmp(additionalPropKey,(char*) OSGI_FRAMEWORK_SERVICE_ID) != 0) { bool propKeyCaseEqual = false; - PROPERTIES_FOR_EACH(serviceProperties, servicePropKey) { + CELIX_PROPERTIES_FOR_EACH(serviceProperties, servicePropKey) { if (strcasecmp(additionalPropKey,servicePropKey) == 0) { const char* val = celix_properties_get(additionalProperties,additionalPropKey,NULL); celix_properties_set(serviceProperties,servicePropKey,val); @@ -250,7 +250,6 @@ static void rsaShm_overlayProperties(celix_properties_t *additionalProperties, c } } } - return; } static bool rsaShm_isConfigTypeMatched(celix_properties_t *properties) { diff --git a/bundles/remote_services/topology_manager/src/topology_manager.c b/bundles/remote_services/topology_manager/src/topology_manager.c index f834ac52..691e72af 100644 --- a/bundles/remote_services/topology_manager/src/topology_manager.c +++ b/bundles/remote_services/topology_manager/src/topology_manager.c @@ -402,8 +402,7 @@ celix_status_t topologyManager_importScopeChanged(void *handle, char *service_na hash_map_entry_pt entry = hashMapIterator_nextEntry(importedServicesIterator); endpoint = hashMapEntry_getKey(entry); - entry = hashMap_getEntry(endpoint->properties, (void *) OSGI_FRAMEWORK_OBJECTCLASS); - char* name = (char *) hashMapEntry_getValue(entry); + const char* name = celix_properties_get(endpoint->properties, OSGI_FRAMEWORK_OBJECTCLASS, ""); // Test if a service with the same name is imported if (strcmp(name, service_name) == 0) { found = true; diff --git a/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp b/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp index 92bc86b6..72bfbd57 100644 --- a/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp +++ b/bundles/remote_services/topology_manager/tms_tst/tms_tests.cpp @@ -397,8 +397,7 @@ extern "C" { for (unsigned int i = 0; i < arrayList_size(epList); i++) { endpoint_description_t *ep = (endpoint_description_t *) arrayList_get(epList, i); celix_properties_t *props = ep->properties; - hash_map_entry_pt entry = hashMap_getEntry(props, (void*)"key2"); - char* value = (char*) hashMapEntry_getValue(entry); + const char* value = celix_properties_get(props, "key2", ""); EXPECT_STREQ("inaetics", value); /* printf("Service: %s ", ep->service); @@ -433,8 +432,7 @@ extern "C" { for (unsigned int i = 0; i < arrayList_size(epList); i++) { endpoint_description_t *ep = (endpoint_description_t *) arrayList_get(epList, i); celix_properties_t *props = ep->properties; - hash_map_entry_pt entry = hashMap_getEntry(props, (void*)"key2"); - char* value = (char*) hashMapEntry_getValue(entry); + const char* value = celix_properties_get(props, "key2", ""); EXPECT_STREQ("inaetics", value); } printf("End: %s\n", __func__); @@ -458,8 +456,7 @@ extern "C" { for (unsigned int i = 0; i < arrayList_size(epList); i++) { endpoint_description_t *ep = (endpoint_description_t *) arrayList_get(epList, i); celix_properties_t *props = ep->properties; - hash_map_entry_pt entry = hashMap_getEntry(props, (void *)"key2"); - char* value = (char*) hashMapEntry_getValue(entry); + const char* value = celix_properties_get(props, "key2", ""); EXPECT_STREQ("inaetics", value); } printf("End: %s\n", __func__); diff --git a/libs/framework/src/service_reference.c b/libs/framework/src/service_reference.c index 18302351..a7faebd7 100644 --- a/libs/framework/src/service_reference.c +++ b/libs/framework/src/service_reference.c @@ -200,17 +200,14 @@ FRAMEWORK_EXPORT celix_status_t serviceReference_getPropertyKeys(service_referen properties_pt props = NULL; serviceRegistration_getProperties(ref->registration, &props); - hash_map_iterator_pt it; int i = 0; - int vsize = hashMap_size(props); + int vsize = celix_properties_size(props); *size = (unsigned int)vsize; *keys = malloc(vsize * sizeof(**keys)); - it = hashMapIterator_create(props); - while (hashMapIterator_hasNext(it)) { - (*keys)[i] = hashMapIterator_nextKey(it); - i++; + const char* key; + CELIX_PROPERTIES_FOR_EACH(props, key) { + (*keys)[i++] = (char*)key; } - hashMapIterator_destroy(it); return status; } diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index e8f51369..6de893b6 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -36,7 +36,7 @@ add_library(utils SHARED src/celix_threads.c src/version.c src/version_range.c - src/properties_v1.c + src/properties.c src/utils.c src/ip_utils.c src/filter.c @@ -114,10 +114,6 @@ if (ENABLE_TESTING) target_include_directories(linked_list_test PRIVATE include_deprecated) target_link_libraries(linked_list_test Celix::utils CppUTest::CppUTest pthread) - add_executable(properties_test private/test/properties_test.cpp) - target_include_directories(properties_test PRIVATE include_deprecated) - target_link_libraries(properties_test CppUTest::CppUTest CppUTest::CppUTestExt Celix::utils pthread) - add_executable(utils_test private/test/utils_test.cpp) target_include_directories(utils_test PRIVATE include_deprecated) target_link_libraries(utils_test CppUTest::CppUTest Celix::utils pthread) @@ -134,13 +130,10 @@ if (ENABLE_TESTING) target_include_directories(version_test PRIVATE include_deprecated) target_link_libraries(version_test CppUTest::CppUTest Celix::utils pthread) - configure_file(private/resources-test/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY) - add_test(NAME run_array_list_test COMMAND array_list_test) add_test(NAME run_hash_map_test COMMAND hash_map_test) add_test(NAME run_celix_threads_test COMMAND celix_threads_test) add_test(NAME run_linked_list_test COMMAND linked_list_test) - add_test(NAME run_properties_test COMMAND properties_test) add_test(NAME run_utils_test COMMAND utils_test) add_test(NAME run_ip_utils_test COMMAND ip_utils_test) add_test(NAME filter_test COMMAND filter_test) @@ -150,7 +143,6 @@ if (ENABLE_TESTING) setup_target_for_coverage(hash_map_test) setup_target_for_coverage(celix_threads_test) setup_target_for_coverage(linked_list_test) - setup_target_for_coverage(properties_test) setup_target_for_coverage(utils_test) setup_target_for_coverage(ip_utils_test) setup_target_for_coverage(filter_test) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 7440357c..dee276f7 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(test_utils src/HashMapTestSuite.cc src/ArrayListTestSuite.cc src/FileUtilsTestSuite.cc + src/PropertiesTestSuite.cc ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} ) @@ -37,6 +38,10 @@ target_link_libraries(test_utils PRIVATE Celix::utils GTest::gtest GTest::gtest_ target_include_directories(test_utils PRIVATE ../src) #for version_private (needs refactoring of test) celix_deprecated_utils_headers(test_utils) +####### Configure properties file for testing ######################################################################### +configure_file(resources/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY) + + ####### generating zip file used for testing ########################################################################## file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/zip_content/top.properties" CONTENT "level=1\n") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/zip_content/subdir/sub.properties" CONTENT "level=2\n") diff --git a/libs/utils/private/resources-test/properties.txt b/libs/utils/gtest/resources/properties.txt similarity index 100% rename from libs/utils/private/resources-test/properties.txt rename to libs/utils/gtest/resources/properties.txt diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 01241b99..d5acac77 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -59,6 +59,22 @@ TEST_F(CxxPropertiesTestSuite, testFillAndLoop) { EXPECT_EQ(5, count); } +TEST_F(CxxPropertiesTestSuite, testLoopForSize0And1) { + celix::Properties props0{}; + for (const auto& pair : props0) { + FAIL() << "Should not get an loop entry with a properties size of 0. got key: " << pair.first; + } + + celix::Properties props1{}; + props1.set("key1", "value1"); + int count = 0; + for (const auto& pair : props1) { + EXPECT_EQ(pair.first, "key1"); + count++; + } + EXPECT_EQ(1, count); +} + TEST_F(CxxPropertiesTestSuite, testCopy) { celix::Properties props{}; diff --git a/libs/utils/private/test/properties_test.cpp b/libs/utils/gtest/src/PropertiesTestSuite.cc similarity index 62% rename from libs/utils/private/test/properties_test.cpp rename to libs/utils/gtest/src/PropertiesTestSuite.cc index 613f6ef2..62bfa8dd 100644 --- a/libs/utils/private/test/properties_test.cpp +++ b/libs/utils/gtest/src/PropertiesTestSuite.cc @@ -16,74 +16,46 @@ * specific language governing permissions and limitations * under the License. */ -/** - * properties_test.cpp - * - * \date Feb 11, 2013 - * \author <a href="mailto:[email protected]">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ - -#include <stdlib.h> -#include <stdio.h> -#include "CppUTest/TestHarness.h" -#include "CppUTest/TestHarness_c.h" -#include "CppUTest/CommandLineTestRunner.h" -#include "CppUTestExt/MockSupport.h" +#include <gtest/gtest.h> -extern "C" { -#include <string.h> - -#include "properties.h" #include "celix_properties.h" -} -int main(int argc, char** argv) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - return RUN_ALL_TESTS(argc, argv); -} +using ::testing::MatchesRegex; -TEST_GROUP(properties) { - celix_properties_t *properties; - - void setup(void) { - } - - void teardown() { - mock().checkExpectations(); - mock().clear(); - } +class PropertiesTestSuite : public ::testing::Test { +public: }; -TEST(properties, create) { - properties = celix_properties_create(); - CHECK(properties); + +TEST_F(PropertiesTestSuite, create) { + auto* properties = celix_properties_create(); + EXPECT_TRUE(properties); celix_properties_destroy(properties); } -TEST(properties, load) { +TEST_F(PropertiesTestSuite, load) { char propertiesFile[] = "resources-test/properties.txt"; - properties = celix_properties_load(propertiesFile); - LONGS_EQUAL(4, hashMap_size(properties)); + auto* properties = celix_properties_load(propertiesFile); + EXPECT_EQ(4, celix_properties_size(properties)); const char keyA[] = "a"; - const char *valueA = celix_properties_get(properties, keyA, NULL); - STRCMP_EQUAL("b", valueA); + const char *valueA = celix_properties_get(properties, keyA, nullptr); + EXPECT_STREQ("b", valueA); const char keyNiceA[] = "nice_a"; - const char *valueNiceA = celix_properties_get(properties, keyNiceA, NULL); - STRCMP_EQUAL("nice_b", valueNiceA); + const char *valueNiceA = celix_properties_get(properties, keyNiceA, nullptr); + EXPECT_STREQ("nice_b", valueNiceA); const char keyB[] = "b"; - const char *valueB = celix_properties_get(properties, keyB, NULL); - STRCMP_EQUAL("c \t d", valueB); + const char *valueB = celix_properties_get(properties, keyB, nullptr); + EXPECT_STREQ("c \t d", valueB); celix_properties_destroy(properties); } -TEST(properties, asLong) { +TEST_F(PropertiesTestSuite, asLong) { celix_properties_t *props = celix_properties_create(); celix_properties_set(props, "t1", "42"); celix_properties_set(props, "t2", "-42"); @@ -92,59 +64,59 @@ TEST(properties, asLong) { celix_properties_set(props, "t5", "bla"); long v = celix_properties_getAsLong(props, "t1", -1); - LONGS_EQUAL(42, v); + EXPECT_EQ(42, v); v = celix_properties_getAsLong(props, "t2", -1); - LONGS_EQUAL(-42, v); + EXPECT_EQ(-42, v); v = celix_properties_getAsLong(props, "t3", -1); - LONGS_EQUAL(-1, v); + EXPECT_EQ(-1, v); v = celix_properties_getAsLong(props, "t4", -1); - LONGS_EQUAL(42, v); + EXPECT_EQ(42, v); v = celix_properties_getAsLong(props, "t5", -1); - LONGS_EQUAL(-1, v); + EXPECT_EQ(-1, v); v = celix_properties_getAsLong(props, "non-existing", -1); - LONGS_EQUAL(-1, v); + EXPECT_EQ(-1, v); celix_properties_destroy(props); } -TEST(properties, store) { +TEST_F(PropertiesTestSuite, store) { char propertiesFile[] = "resources-test/properties_out.txt"; - properties = celix_properties_create(); + auto* properties = celix_properties_create(); char keyA[] = "x"; char keyB[] = "y"; char valueA[] = "1"; char valueB[] = "2"; celix_properties_set(properties, keyA, valueA); celix_properties_set(properties, keyB, valueB); - celix_properties_store(properties, propertiesFile, NULL); + celix_properties_store(properties, propertiesFile, nullptr); celix_properties_destroy(properties); } -TEST(properties, copy) { +TEST_F(PropertiesTestSuite, copy) { char propertiesFile[] = "resources-test/properties.txt"; - properties = celix_properties_load(propertiesFile); - LONGS_EQUAL(4, hashMap_size(properties)); + auto* properties = celix_properties_load(propertiesFile); + EXPECT_EQ(4, celix_properties_size(properties)); celix_properties_t *copy = celix_properties_copy(properties); char keyA[] = "a"; - const char *valueA = celix_properties_get(copy, keyA, NULL); - STRCMP_EQUAL("b", valueA); + const char *valueA = celix_properties_get(copy, keyA, nullptr); + EXPECT_STREQ("b", valueA); const char keyB[] = "b"; - STRCMP_EQUAL("c \t d", celix_properties_get(copy, keyB, NULL)); + EXPECT_STREQ("c \t d", celix_properties_get(copy, keyB, nullptr)); celix_properties_destroy(properties); celix_properties_destroy(copy); } -TEST(properties, getSet) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, getSet) { + auto* properties = celix_properties_create(); char keyA[] = "x"; char keyB[] = "y"; char keyC[] = "z"; @@ -157,34 +129,34 @@ TEST(properties, getSet) { celix_properties_set(properties, keyB, valueB); celix_properties_setWithoutCopy(properties, keyD, valueD); - STRCMP_EQUAL(valueA, celix_properties_get(properties, keyA, NULL)); - STRCMP_EQUAL(valueB, celix_properties_get(properties, keyB, NULL)); - STRCMP_EQUAL(valueC, celix_properties_get(properties, keyC, valueC)); - STRCMP_EQUAL(valueD, celix_properties_get(properties, keyD, NULL)); + EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); + EXPECT_STREQ(valueB, celix_properties_get(properties, keyB, nullptr)); + EXPECT_STREQ(valueC, celix_properties_get(properties, keyC, valueC)); + EXPECT_STREQ(valueD, celix_properties_get(properties, keyD, nullptr)); celix_properties_destroy(properties); } -TEST(properties, setUnset) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, setUnset) { + auto* properties = celix_properties_create(); char keyA[] = "x"; char *keyD = strndup("a", 1); char valueA[] = "1"; char *valueD = strndup("4", 1); celix_properties_set(properties, keyA, valueA); celix_properties_setWithoutCopy(properties, keyD, valueD); - STRCMP_EQUAL(valueA, celix_properties_get(properties, keyA, NULL)); - STRCMP_EQUAL(valueD, celix_properties_get(properties, keyD, NULL)); + EXPECT_STREQ(valueA, celix_properties_get(properties, keyA, nullptr)); + EXPECT_STREQ(valueD, celix_properties_get(properties, keyD, nullptr)); celix_properties_unset(properties, keyA); celix_properties_unset(properties, keyD); - POINTERS_EQUAL(NULL, celix_properties_get(properties, keyA, NULL)); - POINTERS_EQUAL(NULL, celix_properties_get(properties, "a", NULL)); + EXPECT_EQ(nullptr, celix_properties_get(properties, keyA, nullptr)); + EXPECT_EQ(nullptr, celix_properties_get(properties, "a", nullptr)); celix_properties_destroy(properties); } -TEST(properties, longTest) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, longTest) { + auto* properties = celix_properties_create(); celix_properties_set(properties, "a", "2"); celix_properties_set(properties, "b", "-10032L"); @@ -197,24 +169,24 @@ TEST(properties, longTest) { long d = celix_properties_getAsLong(properties, "d", -1L); long e = celix_properties_getAsLong(properties, "e", -1L); - CHECK_EQUAL(2, a); - CHECK_EQUAL(-10032L, b); - CHECK_EQUAL(-1L, c); - CHECK_EQUAL(-1L, d); - CHECK_EQUAL(-1L, e); + EXPECT_EQ(2, a); + EXPECT_EQ(-10032L, b); + EXPECT_EQ(-1L, c); + EXPECT_EQ(-1L, d); + EXPECT_EQ(-1L, e); celix_properties_setLong(properties, "a", 3L); celix_properties_setLong(properties, "b", -4L); a = celix_properties_getAsLong(properties, "a", -1L); b = celix_properties_getAsLong(properties, "b", -1L); - CHECK_EQUAL(3L, a); - CHECK_EQUAL(-4L, b); + EXPECT_EQ(3L, a); + EXPECT_EQ(-4L, b); celix_properties_destroy(properties); } -TEST(properties, boolTest) { - properties = celix_properties_create(); +TEST_F(PropertiesTestSuite, boolTest) { + auto* properties = celix_properties_create(); celix_properties_set(properties, "a", "true"); celix_properties_set(properties, "b", "false"); @@ -228,39 +200,40 @@ TEST(properties, boolTest) { bool e = celix_properties_getAsBool(properties, "e", false); bool f = celix_properties_getAsBool(properties, "f", true); - CHECK_EQUAL(true, a); - CHECK_EQUAL(false, b); - CHECK_EQUAL(true, c); - CHECK_EQUAL(true, d); - CHECK_EQUAL(false, e); - CHECK_EQUAL(true, f); + EXPECT_EQ(true, a); + EXPECT_EQ(false, b); + EXPECT_EQ(true, c); + EXPECT_EQ(true, d); + EXPECT_EQ(false, e); + EXPECT_EQ(true, f); celix_properties_setBool(properties, "a", true); celix_properties_setBool(properties, "b", false); a = celix_properties_getAsBool(properties, "a", false); b = celix_properties_getAsBool(properties, "b", true); - CHECK_EQUAL(true, a); - CHECK_EQUAL(false, b); + EXPECT_EQ(true, a); + EXPECT_EQ(false, b); celix_properties_destroy(properties); } -TEST(properties, sizeAndIteratorTest) { +TEST_F(PropertiesTestSuite, sizeAndIteratorTest) { celix_properties_t *props = celix_properties_create(); - CHECK_EQUAL(0, celix_properties_size(props)); + EXPECT_EQ(0, celix_properties_size(props)); celix_properties_set(props, "a", "1"); celix_properties_set(props, "b", "2"); - CHECK_EQUAL(2, celix_properties_size(props)); + EXPECT_EQ(2, celix_properties_size(props)); celix_properties_set(props, "c", " 3 "); celix_properties_set(props, "d", "4"); - CHECK_EQUAL(4, celix_properties_size(props)); + EXPECT_EQ(4, celix_properties_size(props)); int count = 0; - const char *_key = NULL; - CELIX_PROPERTIES_FOR_EACH(props, _key) { + const char *key; + CELIX_PROPERTIES_FOR_EACH(props, key) { + EXPECT_NE(key, nullptr); count++; } - CHECK_EQUAL(4, count); + EXPECT_EQ(4, count); celix_properties_destroy(props); } diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 4e56860c..3796b004 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -54,14 +54,10 @@ namespace celix { } bool operator==(const celix::PropertiesIterator& rhs) const { - bool sameMap = iter._data1 == rhs.iter._data1; //map - bool sameIndex = iter._data5 == rhs.iter._data5; //index - bool oneIsEnd = end || rhs.end; - if (oneIsEnd) { - return sameMap && end && rhs.end; - } else { - return sameMap && sameIndex; + if (end || rhs.end) { + return end && rhs.end; } + return celix_propertiesIterator_equals(&iter, &rhs.iter); } bool operator!=(const celix::PropertiesIterator& rhs) const { @@ -81,15 +77,16 @@ namespace celix { } void moveToEnd() { - end = true; first = {}; second = {}; + end = true; } + //TODO for C++17 try to update first and second to stringview std::string first{}; std::string second{}; private: - celix_properties_iterator_t iter{nullptr, nullptr, nullptr, 0, 0}; + celix_properties_iterator_t iter{._data = {}}; bool end{false}; }; @@ -414,6 +411,10 @@ namespace celix { return convertToMap(); } #endif + + + //TODO save + //TODO load private: explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {} diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h index 3ff1e906..c704ab60 100644 --- a/libs/utils/include/celix_long_hash_map.h +++ b/libs/utils/include/celix_long_hash_map.h @@ -290,7 +290,11 @@ void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* 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. */ +//TODO test if the macro can be used nested #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))) diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h index 762fcff3..edd1b3cd 100644 --- a/libs/utils/include/celix_properties.h +++ b/libs/utils/include/celix_properties.h @@ -17,6 +17,17 @@ * under the License. */ +/** + * @file celix_properties.h + * @brief Header file for the Celix Properties API. + * + * The Celix Properties API provides a means for storing and manipulating key-value pairs, called properties, + * which can be used to store configuration data or metadata for a service, component, or framework configuration. + * Functions are provided for creating and destroying property sets, loading and storing properties from/to a file + * or stream, and setting, getting, and unsetting individual properties. There are also functions for converting + * property values to various types (e.g. long, bool, double) and for iterating over the properties in a set. + */ + #include <stdio.h> #include <stdbool.h> @@ -29,68 +40,262 @@ extern "C" { #endif -typedef struct hashMap celix_properties_t; //opaque struct, TODO try to make this a celix_properties struct +/** + * @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. + */ +typedef struct celix_properties celix_properties_t; + +//TODO rethink the iter. Maybe this can be more closely coupled to celix_string_hash_map? +//typedef struct celix_properties_iterator { +// //private data +// void* _data1; +// void* _data2; +// void* _data3; +// int _data4; +// int _data5; +//} celix_properties_iterator_t; +/** + * @brief Type representing an iterator for iterating over the properties in a property set. + */ typedef struct celix_properties_iterator { - //private data - void* _data1; - void* _data2; - void* _data3; - int _data4; - int _data5; + //private opaque data + char _data[56]; } celix_properties_iterator_t; -/********************************************************************************************************************** - ********************************************************************************************************************** - * Updated API - ********************************************************************************************************************** - **********************************************************************************************************************/ - +/** + * @brief Creates 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. + * + * @param 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. + * @return A property set containing the properties from the file. + * @retval NULL If an error occurred (e.g. file not found). + */ celix_properties_t* celix_properties_load(const char *filename); + +/** + * @brief Loads properties from a stream. + * + * @param 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). + */ celix_properties_t* celix_properties_loadWithStream(FILE *stream); +/** + * @brief Loads properties from a string. + * + * @param 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). + */ celix_properties_t* celix_properties_loadFromString(const char *input); -void celix_properties_store(celix_properties_t *properties, const char *file, const char *header); +/** + * @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. + */ +void celix_properties_store(celix_properties_t *properties, const char *file, const char *header); //TODO add return status +/** + * @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. + * @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 Sets 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. + */ 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. + * + * @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 + * 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 + * 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. + */ void celix_properties_unset(celix_properties_t *properties, const char *key); +/** + * @brief Makes a copy of a property set. + * + * @param properties The property set to copy. + * @return A copy of the given property set. + */ celix_properties_t* celix_properties_copy(const celix_properties_t *properties); +/** + * @brief Gets the value of a property as a long integer. + * + * @param props The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set or cannot be converted to a long. + * @return The value of the property as a long, or the default value if the property is not set or cannot be converted + * to a long. + */ long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue); + +/** + * @brief Sets the value of a property to a long integer. + * + * @param props The property set to modify. + * @param key The key of the property to set. + * @param value The long value to set the property to. + */ void celix_properties_setLong(celix_properties_t *props, const char *key, long value); +/** + * @brief Gets the value of a property as a boolean. + * + * @param props The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set or cannot be converted to a boolean. + * @return The value of the property as a boolean, or the default value if the property is not set or cannot be + * converted to a boolean. + */ bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue); -void celix_properties_setBool(celix_properties_t *props, const char *key, bool val); +/** + * @brief Sets the value of a property to a boolean. + * + * @param props The property set to modify. + * @param key The key of the property to set. + * @param val The boolean value to set the property to. + */ +void celix_properties_setBool(celix_properties_t *props, const char *key, bool val); +/** + * @brief Sets the value of a property to a double. + * + * @param props The property set to modify. + * @param key The key of the property to set. + * @param val The double value to set the property to. + */ void celix_properties_setDouble(celix_properties_t *props, const char *key, double val); + +/** + * @brief Gets the value of a property as a double. + * + * @param props The property set to search. + * @param key The key of the property to get. + * @param defaultValue The value to return if the property is not set or cannot be converted to a double. + * @return The value of the property as a double, or the default value if the property is not set or cannot be converted to a double. + */ double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue); +/** + * @brief Gets the number of properties in a property set. + * + * @param properties The property set to get the size of. + * @return The number of properties in the property set. + */ int celix_properties_size(const celix_properties_t *properties); +/** + * @brief Constructs a new iterator for iterating over the properties in a property set. + * + * @param properties The property set to iterate over. + * @return A new iterator for the given property set. + */ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties); + +/** + * @brief Determines whether the iterator has a next property. + * + * @param iter The iterator to check. + * @return true if the iterator has a next property, false otherwise. + */ bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter); + +/** + * @brief Gets the next key in the iterator. + * @param iter The iterator to get the next key from. + * @return The next key in the iterator. + */ const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter); -celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter); -#define CELIX_PROPERTIES_FOR_EACH(props, key) \ - for(celix_properties_iterator_t iter = celix_propertiesIterator_construct(props); \ - celix_propertiesIterator_hasNext(&iter), (key) = celix_propertiesIterator_nextKey(&iter);) +/** + * @brief Gets the property set being iterated over. + * + * @param iter The iterator to get the property set from. + * @return The property set being iterated over. + */ +celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter); +/** + * @brief Determines whether two iterators are equal. + * + * @param a The first iterator to compare. + * @param b The second iterator to compare. + * @return true if the iterators are equal, false otherwise. + */ +bool celix_propertiesIterator_equals(const celix_properties_iterator_t* a, const celix_properties_iterator_t* b); +/** + * @brief Macro for iterating over the properties in a property set. + * + * @param props The property set to iterate over. + * @param key The variable to use for the current key in the loop. + * + * Example usage: + * @code{.c} + * celix_properties_t *props = celix_properties_create(); + * celix_properties_set(props, "key1", "value1"); + * celix_properties_set(props, "key2", "value2"); + * celix_properties_set(props, "key3", "value3"); + * + * const char* key; + * CELIX_PROPERTIES_FOR_EACH(props, key) { + * printf("%s = %s\n", key, celix_properties_get(props, key, "")); + * } + * @endcode + * Output: + * @code{.c} + * key1 = value1 + * key2 = value2 + * key3 = value3 + * @endcode +*/ +#define CELIX_PROPERTIES_FOR_EACH(props, key) \ + for(celix_properties_iterator_t iter_##key = celix_propertiesIterator_construct(props); \ + celix_propertiesIterator_hasNext(&iter_##key), (key) = celix_propertiesIterator_nextKey(&iter_##key);) #ifdef __cplusplus } diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h index c402be4c..97c1735b 100644 --- a/libs/utils/include/celix_string_hash_map.h +++ b/libs/utils/include/celix_string_hash_map.h @@ -291,6 +291,7 @@ 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 * celix_string_hash_map_t* map = ... @@ -298,7 +299,11 @@ void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* 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. */ +//TODO test if the macro can be used nested #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))) diff --git a/libs/utils/include_deprecated/properties.h b/libs/utils/include_deprecated/properties.h index 00e9c785..8bcbc3db 100644 --- a/libs/utils/include_deprecated/properties.h +++ b/libs/utils/include_deprecated/properties.h @@ -37,8 +37,8 @@ extern "C" { #endif -typedef hash_map_pt properties_pt __attribute__((deprecated("properties is deprecated use celix_properties instead"))); -typedef hash_map_t properties_t __attribute__((deprecated("properties is deprecated use celix_properties instead"))); +typedef struct celix_properties* properties_pt __attribute__((deprecated("properties is deprecated use celix_properties instead"))); +typedef struct celix_properties properties_t __attribute__((deprecated("properties is deprecated use celix_properties instead"))); UTILS_EXPORT celix_properties_t* properties_create(void); @@ -62,10 +62,11 @@ UTILS_EXPORT void properties_unset(celix_properties_t *properties, const char *k UTILS_EXPORT celix_status_t properties_copy(celix_properties_t *properties, celix_properties_t **copy); +/** TODO refactor #define PROPERTIES_FOR_EACH(props, key) \ for(hash_map_iterator_t iter = hashMapIterator_construct(props); \ hashMapIterator_hasNext(&iter), (key) = (const char*)hashMapIterator_nextKey(&iter);) - +*/ #ifdef __cplusplus } diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c new file mode 100644 index 00000000..a8321124 --- /dev/null +++ b/libs/utils/src/properties.c @@ -0,0 +1,523 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "properties.h" +#include "celix_properties.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdbool.h> +#include <errno.h> + +#include "celix_build_assert.h" +#include "utils.h" //TODO try to remove +#include "celix_utils.h" +#include "celix_string_hash_map.h" + + +#define PROPERTIES_INITIAL_HASHMAP_CAPACITY 10 +#define SHORT_PROPERTIES_OPTIMIZATION_SIZE 1024 + +struct celix_properties { + celix_string_hash_map_t* map; + + /** + * buffer used to store the first key/value entries so that no additional memory allocation is needed. + */ + char buffer[SHORT_PROPERTIES_OPTIMIZATION_SIZE]; +}; + + +#define MALLOC_BLOCK_SIZE 5 + +static void parseLine(const char* line, celix_properties_t *props); + +properties_pt properties_create(void) { + return celix_properties_create(); +} + +void properties_destroy(properties_pt properties) { + celix_properties_destroy(properties); +} + +properties_pt properties_load(const char* filename) { + return celix_properties_load(filename); +} + +properties_pt properties_loadWithStream(FILE *file) { + return celix_properties_loadWithStream(file); +} + +properties_pt properties_loadFromString(const char *input){ + return celix_properties_loadFromString(input); +} + + +/** + * Header is ignored for now, cannot handle comments yet + */ +void properties_store(properties_pt properties, const char* filename, const char* header) { + return celix_properties_store(properties, filename, header); +} + +celix_status_t properties_copy(properties_pt properties, properties_pt *out) { + celix_properties_t *copy = celix_properties_copy(properties); + *out = copy; + return copy == NULL ? CELIX_BUNDLE_EXCEPTION : CELIX_SUCCESS; +} + +const char* properties_get(properties_pt properties, const char* key) { + return celix_properties_get(properties, key, NULL); +} + +const char* properties_getWithDefault(properties_pt properties, const char* key, const char* defaultValue) { + return celix_properties_get(properties, key, defaultValue); +} + +void properties_set(properties_pt properties, const char* key, const char* value) { + celix_properties_set(properties, key, value); +} + +void properties_unset(properties_pt properties, const char* key) { + celix_properties_unset(properties, key); +} + +static void updateBuffers(char **key, char ** value, char **output, int outputPos, int *key_len, int *value_len) { + if (*output == *key) { + if (outputPos == (*key_len) - 1) { + (*key_len) += MALLOC_BLOCK_SIZE; + *key = realloc(*key, *key_len); + *output = *key; + } + } + else { + if (outputPos == (*value_len) - 1) { + (*value_len) += MALLOC_BLOCK_SIZE; + *value = realloc(*value, *value_len); + *output = *value; + } + } +} + +static void parseLine(const char* line, celix_properties_t *props) { + int linePos = 0; + bool precedingCharIsBackslash = false; + bool isComment = false; + int outputPos = 0; + char *output = NULL; + int key_len = MALLOC_BLOCK_SIZE; + int value_len = MALLOC_BLOCK_SIZE; + linePos = 0; + precedingCharIsBackslash = false; + isComment = false; + output = NULL; + outputPos = 0; + + //Ignore empty lines + if (line[0] == '\n' && line[1] == '\0') { + return; + } + + char *key = calloc(1, key_len); + char *value = calloc(1, value_len); + key[0] = '\0'; + value[0] = '\0'; + + while (line[linePos] != '\0') { + if (line[linePos] == ' ' || line[linePos] == '\t') { + if (output == NULL) { + //ignore + linePos += 1; + continue; + } + } + else { + if (output == NULL) { + output = key; + } + } + if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == '#' || line[linePos] == '!') { + if (precedingCharIsBackslash) { + //escaped special character + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + precedingCharIsBackslash = false; + } + else { + if (line[linePos] == '#' || line[linePos] == '!') { + if (outputPos == 0) { + isComment = true; + break; + } + else { + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + } + else { // = or : + if (output == value) { //already have a seperator + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + else { + output[outputPos++] = '\0'; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + output = value; + outputPos = 0; + } + } + } + } + else if (line[linePos] == '\\') { + if (precedingCharIsBackslash) { //double backslash -> backslash + output[outputPos++] = '\\'; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + precedingCharIsBackslash = true; + } + else { //normal character + precedingCharIsBackslash = false; + output[outputPos++] = line[linePos]; + updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len); + } + linePos += 1; + } + if (output != NULL) { + output[outputPos] = '\0'; + } + + if (!isComment) { + //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value)); + celix_properties_set(props, utils_stringTrim(key), utils_stringTrim(value)); + } + if(key) { + free(key); + } + if(value) { + free(value); + } + +} + + + +/********************************************************************************************************************** + ********************************************************************************************************************** + * Updated API + ********************************************************************************************************************** + **********************************************************************************************************************/ + +static void celix_properties_removeEntryCallback(void* handle, const char* key, celix_hash_map_value_t val) { + //celix_properties_t* props = handle; + //TODO add check of short properties optimization buffer + free((char*)key); + free(val.ptrValue); +} + + +celix_properties_t* celix_properties_create(void) { + celix_properties_t* props = malloc(sizeof(*props)); + if (props != NULL) { + celix_string_hash_map_create_options_t opts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS; + opts.storeKeysWeakly = true; + opts.initialCapacity = PROPERTIES_INITIAL_HASHMAP_CAPACITY; + opts.removedCallbackData = props; + opts.removedCallback = celix_properties_removeEntryCallback; + props->map = celix_stringHashMap_createWithOptions(&opts); + } + return props; +} + +void celix_properties_destroy(celix_properties_t *props) { + if (props != NULL) { + celix_stringHashMap_destroy(props->map); + free(props); + } +} + +celix_properties_t* celix_properties_load(const char *filename) { + FILE *file = fopen(filename, "r"); + if (file == NULL) { + return NULL; + } + celix_properties_t *props = celix_properties_loadWithStream(file); + fclose(file); + return props; +} + +celix_properties_t* celix_properties_loadWithStream(FILE *file) { + celix_properties_t *props = NULL; + + if (file != NULL ) { + char *saveptr; + char *filebuffer = NULL; + char *line = NULL; + size_t file_size = 0; + + props = celix_properties_create(); + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (file_size > 0) { + filebuffer = calloc(file_size + 1, sizeof(char)); + if (filebuffer) { + size_t rs = fread(filebuffer, sizeof(char), file_size, file); + if (rs != file_size) { + fprintf(stderr,"fread read only %lu bytes out of %lu\n", (long unsigned int) rs, (long unsigned int) file_size); + } + filebuffer[file_size]='\0'; + line = strtok_r(filebuffer, "\n", &saveptr); + while (line != NULL) { + parseLine(line, props); + line = strtok_r(NULL, "\n", &saveptr); + } + free(filebuffer); + } + } + } + + return props; +} + +celix_properties_t* celix_properties_loadFromString(const char *input) { + celix_properties_t *props = celix_properties_create(); + + char *in = strdup(input); + char *line = NULL; + char *saveLinePointer = NULL; + + bool firstTime = true; + do { + if (firstTime){ + line = strtok_r(in, "\n", &saveLinePointer); + firstTime = false; + }else { + line = strtok_r(NULL, "\n", &saveLinePointer); + } + + if (line == NULL){ + break; + } + + parseLine(line, props); + } while(line != NULL); + + free(in); + + return props; +} + +/** + * @brief Store properties string to file and escape the characters '#', '!', '=' and ':' if encountered. + */ +static void celix_properties_storeEscapedString(FILE* file, const char* str) { + for (int i = 0; i < strlen(str); i += 1) { + if (str[i] == '#' || str[i] == '!' || str[i] == '=' || str[i] == ':') { + fputc('\\', file); + } + fputc(str[i], file); + } +} + +void celix_properties_store(celix_properties_t *properties, const char *filename, const char *header) { + FILE *file = fopen (filename, "w+" ); + + if (file == NULL) { + perror("File is null"); + return; + } + + CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { + const char* val = iter.value.ptrValue; + celix_properties_storeEscapedString(file, iter.key); + fputc('=', file); + celix_properties_storeEscapedString(file, val); + fputc('\n', file); + + } + fclose(file); +} + +celix_properties_t* celix_properties_copy(const celix_properties_t *properties) { + celix_properties_t *copy = celix_properties_create(); + if (properties == NULL) { + return copy; + } + + CELIX_STRING_HASH_MAP_ITERATE(properties->map, iter) { + celix_properties_set(copy, iter.key, iter.value.ptrValue); + } + return copy; +} + +const char* celix_properties_get(const celix_properties_t *properties, const char *key, const char *defaultValue) { + const char* value = NULL; + if (properties != NULL) { + value = celix_stringHashMap_get(properties->map, key); + } + return value == NULL ? defaultValue : value; +} + +void celix_properties_set(celix_properties_t *properties, const char *key, const char *value) { + if (properties != NULL) { + celix_stringHashMap_put(properties->map, celix_utils_strdup(key), celix_utils_strdup(value)); + } +} + +void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value) { + if (properties != NULL) { + if (properties != NULL) { + celix_stringHashMap_put(properties->map, key, value); + } + } +} + +void celix_properties_unset(celix_properties_t *properties, const char *key) { + if (properties != NULL) { + celix_stringHashMap_remove(properties->map, key); + } +} + +long celix_properties_getAsLong(const celix_properties_t *props, const char *key, long defaultValue) { + long result = defaultValue; + const char *val = celix_properties_get(props, key, NULL); + if (val != NULL) { + char *enptr = NULL; + errno = 0; + long r = strtol(val, &enptr, 10); + if (enptr != val && errno == 0) { + result = r; + } + } + return result; +} + +void celix_properties_setLong(celix_properties_t *props, const char *key, long value) { + char buf[32]; //should be enough to store long long int + int writen = snprintf(buf, 32, "%li", value); + if (writen <= 31) { + celix_properties_set(props, key, buf); + } else { + fprintf(stderr,"buf to small for value '%li'\n", value); + } +} + +double celix_properties_getAsDouble(const celix_properties_t *props, const char *key, double defaultValue) { + double result = defaultValue; + const char *val = celix_properties_get(props, key, NULL); + if (val != NULL) { + char *enptr = NULL; + errno = 0; + double r = strtod(val, &enptr); + if (enptr != val && errno == 0) { + result = r; + } + } + return result; +} + +void celix_properties_setDouble(celix_properties_t *props, const char *key, double val) { + char buf[32]; //should be enough to store long long int + int writen = snprintf(buf, 32, "%f", val); + if (writen <= 31) { + celix_properties_set(props, key, buf); + } else { + fprintf(stderr,"buf to small for value '%f'\n", val); + } +} + +bool celix_properties_getAsBool(const celix_properties_t *props, const char *key, bool defaultValue) { + bool result = defaultValue; + const char *val = celix_properties_get(props, key, NULL); + if (val != NULL) { + char buf[32]; + snprintf(buf, 32, "%s", val); + char *trimmed = utils_stringTrim(buf); + if (strncasecmp("true", trimmed, strlen("true")) == 0) { + result = true; + } else if (strncasecmp("false", trimmed, strlen("false")) == 0) { + result = false; + } + } + return result; +} + +void celix_properties_setBool(celix_properties_t *props, const char *key, bool val) { + celix_properties_set(props, key, val ? "true" : "false"); +} + +int celix_properties_size(const celix_properties_t *properties) { + return (int)celix_stringHashMap_size(properties->map); +} + +typedef struct { + celix_string_hash_map_iterator_t mapIter; + const celix_properties_t* props; +} celix_properties_iterator_internal_t; + +celix_properties_iterator_t celix_propertiesIterator_construct(const celix_properties_t *properties) { + CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(celix_properties_iterator_t)); + celix_properties_iterator_internal_t internalIter; + internalIter.mapIter = celix_stringHashMap_begin(properties->map); + internalIter.props = properties; + + celix_properties_iterator_t iter; + memset(&iter._data, 0, sizeof(iter._data)); + memcpy(iter._data, &internalIter, sizeof(internalIter)); + return iter; +} + +bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter, sizeof(internalIter)); + return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter); +} + +const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter, sizeof(internalIter)); + + //note assigning key first and then move the next, because celix string hash map iter start at the beginning + const char* key = internalIter.mapIter.key; + celix_stringHashMapIterator_next(&internalIter.mapIter); + + memcpy(iter, &internalIter, sizeof(internalIter)); + return key; +} + +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)); + celix_properties_iterator_internal_t internalIterB; + memcpy(&internalIterB, b, sizeof(internalIterB)); + return internalIterA.props == internalIterB.props && + internalIterA.mapIter.key == internalIterB.mapIter.key; +} + + +//TODO can the return be const? +//TODO and can this be removed -> look into C++ PropetiesIter +celix_properties_t* celix_propertiesIterator_properties(celix_properties_iterator_t *iter) { + celix_properties_iterator_internal_t internalIter; + memcpy(&internalIter, iter, sizeof(internalIter)); + return (celix_properties_t*)internalIter.props; +} diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c index 4ebb938c..7bbe2f4c 100644 --- a/libs/utils/src/utils.c +++ b/libs/utils/src/utils.c @@ -60,7 +60,7 @@ bool celix_utils_stringEquals(const char* a, const char* b) { } else if (a == NULL || b == NULL) { return false; } else { - return strncmp(a, b, 1024*124*10) == 0; + return strncmp(a, b, CELIX_UTILS_MAX_STRLEN) == 0; } }
