This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/674-use-properties-type-in-filter in repository https://gitbox.apache.org/repos/asf/celix.git
commit 1e9ec2f78412744ea08bd0357aef67a6e4b67abb Author: Pepijn Noltes <[email protected]> AuthorDate: Sun Nov 19 19:01:41 2023 +0100 Refactor filter create / parse functions --- libs/utils/benchmark/src/FilterBenchmark.cc | 11 +- libs/utils/gtest/src/FilterTestSuite.cc | 487 +++++++++++++++------------- libs/utils/include/celix_filter.h | 10 +- libs/utils/src/filter.c | 384 +++++++++++----------- 4 files changed, 468 insertions(+), 424 deletions(-) diff --git a/libs/utils/benchmark/src/FilterBenchmark.cc b/libs/utils/benchmark/src/FilterBenchmark.cc index 43b4cdef..1a415350 100644 --- a/libs/utils/benchmark/src/FilterBenchmark.cc +++ b/libs/utils/benchmark/src/FilterBenchmark.cc @@ -176,10 +176,16 @@ static void FilterBenchmark_versionRangeFilter(benchmark::State& state) { benchmark.testFilter(state, filter, true); } +static void FilterBenchmark_substringFilter(benchmark::State& state) { + FilterBenchmark benchmark{state}; + celix::Filter filter{"(&(version_key1>=1.0.0)(version_key1<2.0.0))"}; + benchmark.testFilter(state, filter, true); +} + + static void FilterBenchmark_complexFilter(benchmark::State& state) { FilterBenchmark benchmark{state}; - celix::Filter filter{"(&(version_key1>=1.0.0)(version_key1<2.0.0)(str_key1=str_value1)(long_key1=1)(double_key1>=0." - "9)(bool_key1=true))"}; + celix::Filter filter{"(str_key1=*value1)"}; benchmark.testFilter(state, filter, true); } @@ -206,4 +212,5 @@ CELIX_BENCHMARK(FilterBenchmark_versionGreaterEqualFilter); //Specials CELIX_BENCHMARK(FilterBenchmark_versionRangeFilter); +CELIX_BENCHMARK(FilterBenchmark_substringFilter); CELIX_BENCHMARK(FilterBenchmark_complexFilter); diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc index 55fb5e2c..2dd171e2 100644 --- a/libs/utils/gtest/src/FilterTestSuite.cc +++ b/libs/utils/gtest/src/FilterTestSuite.cc @@ -18,346 +18,356 @@ */ #include <gtest/gtest.h> + #include "celix_filter.h" #include "celix_utils.h" +#include "celix_err.h" +class FilterTestSuite : public ::testing::Test { + public: + FilterTestSuite() { + celix_err_resetErrors(); + } -class FilterTestSuite : public ::testing::Test {}; + ~FilterTestSuite() override { + celix_err_printErrors(stderr, nullptr, nullptr); + } +}; -TEST_F(FilterTestSuite, create_destroy){ +TEST_F(FilterTestSuite, CreateDestroyTest) { const char* filter_str = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3)))"; - celix_filter_t * get_filter; + celix_filter_t* filter; - get_filter = celix_filter_create(filter_str); - ASSERT_TRUE(get_filter != NULL); + filter = celix_filter_create(filter_str); + ASSERT_TRUE(filter != nullptr); - celix_filter_destroy(get_filter); + celix_filter_destroy(filter); } -TEST_F(FilterTestSuite, create_fail_missing_opening_brackets){ - celix_filter_t * get_filter; +TEST_F(FilterTestSuite, MissingOpenBracketsCreateTest) { + celix_filter_t* filter; - //test missing opening brackets in main filter - const char *filter_str1 = "&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3))"; - get_filter = celix_filter_create(filter_str1); - ASSERT_TRUE(get_filter == NULL); + // test missing opening brackets in main filter + const char* str1 = "&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3))"; + filter = celix_filter_create(str1); + ASSERT_TRUE(filter == nullptr); - //test missing opening brackets in AND comparator - const char *filter_str2 = "(&test_attr1=attr1|(test_attr2=attr2)(test_attr3=attr3))"; - get_filter = celix_filter_create(filter_str2); - ASSERT_TRUE(get_filter == NULL); + // test missing opening brackets in AND comparator + const char* str2 = "(&test_attr1=attr1|(test_attr2=attr2)(test_attr3=attr3))"; + filter = celix_filter_create(str2); + ASSERT_TRUE(filter == nullptr); - //test missing opening brackets in AND comparator - const char *filter_str3 = "(&(test_attr1=attr1)(|test_attr2=attr2(test_attr3=attr3))"; - get_filter = celix_filter_create(filter_str3); - ASSERT_TRUE(get_filter == NULL); + // test missing opening brackets in AND comparator + const char* str3 = "(&(test_attr1=attr1)(|test_attr2=attr2(test_attr3=attr3))"; + filter = celix_filter_create(str3); + ASSERT_TRUE(filter == nullptr); - //test missing opening brackets in NOT comparator - const char *filter_str4 = "(&(test_attr1=attr1)(!test_attr2=attr2)"; - get_filter = celix_filter_create(filter_str4); - ASSERT_TRUE(get_filter == NULL); + // test missing opening brackets in NOT comparator + const char* str4 = "(&(test_attr1=attr1)(!test_attr2=attr2)"; + filter = celix_filter_create(str4); + ASSERT_TRUE(filter == nullptr); } -TEST_F(FilterTestSuite, create_fail_missing_closing_brackets){ - char * filter_str; - celix_filter_t * get_filter; - //test missing closing brackets in substring - filter_str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3"); - get_filter = celix_filter_create(filter_str); - ASSERT_TRUE(get_filter == NULL); - free(filter_str); - - //test missing closing brackets in value - filter_str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=attr3"); - get_filter = celix_filter_create(filter_str); - ASSERT_TRUE(get_filter == NULL); - free(filter_str); +TEST_F(FilterTestSuite, MissingClosingBracketsCreateTest) { + char* str; + celix_filter_t* filter; + // test missing closing brackets in substring + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3"); + filter = celix_filter_create(str); + ASSERT_TRUE(filter == nullptr); + free(str); + + // test missing closing brackets in value + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=attr3"); + filter = celix_filter_create(str); + ASSERT_TRUE(filter == nullptr); + free(str); } -TEST_F(FilterTestSuite, create_fail_invalid_closing_brackets) { - char *filter_str; - celix_filter_t *get_filter; - - //test missing closing brackets in substring - filter_str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=at(tr3)))"); - get_filter = celix_filter_create(filter_str); - ASSERT_TRUE(get_filter == NULL); - free(filter_str); - - //test missing closing brackets in value - filter_str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=att(r3)))"); - get_filter = celix_filter_create(filter_str); - ASSERT_TRUE(get_filter == NULL); - free(filter_str); +TEST_F(FilterTestSuite, InvalidClosingBracketsCreateTest) { + char* str; + celix_filter_t* filter; + + // test missing closing brackets in substring + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=at(tr3)))"); + filter = celix_filter_create(str); + ASSERT_TRUE(filter == nullptr); + free(str); + + // test missing closing brackets in value + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=att(r3)))"); + filter = celix_filter_create(str); + ASSERT_TRUE(filter == nullptr); + free(str); } -TEST_F(FilterTestSuite, create_misc) { - celix_filter_t *get_filter; - //test trailing chars - const char *filter_str1 = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3))) oh no! trailing chars"; - get_filter = celix_filter_create(filter_str1); - ASSERT_TRUE(get_filter == NULL); - - //test half APPROX operator (should be "~=", instead is "~") - const char *filter_str2 = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3~attr3)))"; - get_filter = celix_filter_create(filter_str2); - ASSERT_TRUE(get_filter == NULL); - - //test PRESENT operator with trailing chars (should just register as substrings: "*" and "attr3") - const char *filter_str3 = "(test_attr3=*attr3)"; - get_filter = celix_filter_create(filter_str3); - ASSERT_TRUE(get_filter != NULL); - ASSERT_EQ(CELIX_FILTER_OPERAND_SUBSTRING, get_filter->operand); - ASSERT_EQ(2, celix_arrayList_size((celix_array_list_t *) get_filter->children)); - celix_filter_destroy(get_filter); - - //test parsing a attribute of 0 length - const char *filter_str4 = "(>=attr3)"; - get_filter = celix_filter_create(filter_str4); - ASSERT_TRUE(get_filter == NULL); - - //test parsing a value of 0 length - const char *filter_str5 = "(test_attr3>=)"; - get_filter = celix_filter_create(filter_str5); - ASSERT_TRUE(get_filter == NULL); - - //test parsing a value with a escaped closing bracket "\)" - const char *filter_str6 = "(test_attr3>=strWith\\)inIt)"; - get_filter = celix_filter_create(filter_str6); - ASSERT_TRUE(get_filter != NULL); - ASSERT_STREQ("strWith)inIt", (char *) get_filter->value); - celix_filter_destroy(get_filter); - - //test parsing a substring with a escaped closing bracket "\)" - const char *filter_str7 = "(test_attr3=strWith\\)inIt)"; - get_filter = celix_filter_create(filter_str7); - ASSERT_TRUE(get_filter != NULL); - ASSERT_STREQ("strWith)inIt", (char *) get_filter->value); - celix_filter_destroy(get_filter); +TEST_F(FilterTestSuite, MiscInvalidCreateTest) { + celix_filter_t* filter; + // test trailing chars + const char* str1 = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3))) oh no! trailing chars"; + filter = celix_filter_create(str1); + ASSERT_TRUE(filter == nullptr); + + // test half APPROX operator (should be "~=", instead is "~") + const char* str2 = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3~attr3)))"; + filter = celix_filter_create(str2); + ASSERT_TRUE(filter == nullptr); + + // test PRESENT operator with trailing chars (should just register as substrings: "*" and "attr3") + const char* str3 = "(test_attr3=*attr3)"; + filter = celix_filter_create(str3); + ASSERT_TRUE(filter != nullptr); + ASSERT_EQ(CELIX_FILTER_OPERAND_SUBSTRING, filter->operand); + ASSERT_EQ(2, celix_arrayList_size((celix_array_list_t*)filter->children)); + celix_filter_destroy(filter); + + // test parsing an attribute of 0 length + const char* str4 = "(>=attr3)"; + filter = celix_filter_create(str4); + ASSERT_TRUE(filter == nullptr); + + // test parsing a value of 0 length + const char* str5 = "(test_attr3>=)"; + filter = celix_filter_create(str5); + ASSERT_TRUE(filter == nullptr); + + // test parsing a value with a escaped closing bracket "\)" + const char* str6 = "(test_attr3>=strWith\\)inIt)"; + filter = celix_filter_create(str6); + ASSERT_TRUE(filter != nullptr); + ASSERT_STREQ("strWith)inIt", (char*)filter->value); + celix_filter_destroy(filter); + + // test parsing a substring with an escaped closing bracket "\)" + const char* str7 = "(test_attr3=strWith\\)inIt)"; + filter = celix_filter_create(str7); + ASSERT_TRUE(filter != nullptr); + ASSERT_STREQ("strWith)inIt", (char*)filter->value); + celix_filter_destroy(filter); } -TEST_F(FilterTestSuite, match_comparators) { - char *filter_str; - celix_filter_t *filter; - celix_properties_t *props = celix_properties_create(); - char *key = celix_utils_strdup("test_attr1"); - char *val = celix_utils_strdup("attr1"); - char *key2 = celix_utils_strdup("test_attr2"); - char *val2 = celix_utils_strdup("attr2"); +TEST_F(FilterTestSuite, MatchEqualTest) { + char* str; + celix_filter_t* filter; + celix_properties_t* props = celix_properties_create(); + char* key = celix_utils_strdup("test_attr1"); + char* val = celix_utils_strdup("attr1"); + char* key2 = celix_utils_strdup("test_attr2"); + char* val2 = celix_utils_strdup("attr2"); celix_properties_set(props, key, val); celix_properties_set(props, key2, val2); - //test AND - filter_str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(!(test_attr3=attr3))))"); - filter = celix_filter_create(filter_str); + // test AND + str = celix_utils_strdup("(&(test_attr1=attr1)(|(test_attr2=attr2)(!(test_attr3=attr3))))"); + filter = celix_filter_create(str); bool result = celix_filter_match(filter, props); ASSERT_TRUE(result); - //test AND false + // test AND false celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1=attr1)(test_attr1=attr2))"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1=attr1)(test_attr1=attr2))"); + filter = celix_filter_create(str); ASSERT_TRUE(filter == nullptr); result = celix_filter_match(filter, props); ASSERT_TRUE(result); -//cleanup + // cleanup celix_properties_destroy(props); celix_filter_destroy(filter); - free(filter_str); + free(str); free(key); free(key2); free(val); free(val2); } -TEST_F(FilterTestSuite, match_operators) { - char *filter_str; - celix_filter_t *filter; - celix_properties_t *props = celix_properties_create(); - char *key = celix_utils_strdup("test_attr1"); - char *val = celix_utils_strdup("attr1"); - char *key2 = celix_utils_strdup("test_attr2"); - char *val2 = celix_utils_strdup("attr2"); +TEST_F(FilterTestSuite, MatchTest) { + char* str; + celix_filter_t* filter; + celix_properties_t* props = celix_properties_create(); + char* key = celix_utils_strdup("test_attr1"); + char* val = celix_utils_strdup("attr1"); + char* key2 = celix_utils_strdup("test_attr2"); + char* val2 = celix_utils_strdup("attr2"); celix_properties_set(props, key, val); celix_properties_set(props, key2, val2); // Test EQUALS - filter_str = celix_utils_strdup("(test_attr1=attr1)"); - filter = celix_filter_create(filter_str); + str = celix_utils_strdup("(test_attr1=attr1)"); + filter = celix_filter_create(str); bool result = celix_filter_match(filter, props); EXPECT_TRUE(result); // Test EQUALS false celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1=falseString)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1=falseString)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1~=attr1)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1~=attr1)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1~=ATTR1)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1~=ATTR1)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); // Test PRESENT celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1=*)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1=*)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); // Test NOT PRESENT celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr3=*)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr3=*)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); // Test NOT PRESENT celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(!(test_attr3=*))"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(!(test_attr3=*))"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); // Test LESSEQUAL less celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1<=attr5)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1<=attr5)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); // Test LESSEQUAL equals celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2<=attr2)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2<=attr2)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); - //test LESSEQUAL false + // test LESSEQUAL false celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2<=attr1)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2<=attr1)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //test GREATEREQUAL greater + // test GREATEREQUAL greater celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2>=attr1)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2>=attr1)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); - //test GREATEREQUAL equals + // test GREATEREQUAL equals celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2>=attr2)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2>=attr2)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); - //test GREATEREQUAL false + // test GREATEREQUAL false celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1>=attr5)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1>=attr5)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //test LESS less + // test LESS less celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1<attr5)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1<attr5)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); - //test LESS equals + // test LESS equals celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2<attr2)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2<attr2)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //test LESS false + // test LESS false celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2<attr1)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2<attr1)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //test GREATER greater + // test GREATER greater celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2>attr1)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2>attr1)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); - //test GREATER equals + // test GREATER equals celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr2>attr2)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr2>attr2)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //test GREATER false + // test GREATER false celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1>attr5)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1>attr5)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //test SUBSTRING equals + // test SUBSTRING equals celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1=attr*)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1=attr*)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_TRUE(result); celix_filter_destroy(filter); - free(filter_str); - filter_str = celix_utils_strdup("(test_attr1=attr*charsNotPresent)"); - filter = celix_filter_create(filter_str); + free(str); + str = celix_utils_strdup("(test_attr1=attr*charsNotPresent)"); + filter = celix_filter_create(str); result = celix_filter_match(filter, props); EXPECT_FALSE(result); - //cleanup + // cleanup celix_properties_destroy(props); celix_filter_destroy(filter); - free(filter_str); + free(str); free(key); free(key2); free(val); free(val2); } -TEST_F(FilterTestSuite, match_recursion) { - auto* filter_str = "(&(test_attr1=attr1)(|(&(test_attr2=attr2)(!(&(test_attr1=attr1)(test_attr3=attr3))))(test_attr3=attr3)))"; - auto* filter = celix_filter_create(filter_str); +TEST_F(FilterTestSuite, MatchRecursionTest) { + auto* str = "(&(test_attr1=attr1)(|(&(test_attr2=attr2)(!(&(test_attr1=attr1)(test_attr3=attr3))))(test_attr3=attr3)))"; + auto* filter = celix_filter_create(str); auto* props = celix_properties_create(); auto* key = "test_attr1"; auto* val = "attr1"; @@ -372,9 +382,9 @@ TEST_F(FilterTestSuite, match_recursion) { celix_filter_destroy(filter); } -TEST_F(FilterTestSuite, match_false) { - auto* filter_str = "(&(test_attr1=attr1)(&(test_attr2=attr2)(test_attr3=attr3)))"; - celix_filter_t* filter = celix_filter_create(filter_str); +TEST_F(FilterTestSuite, FalseMatchTest) { + auto* str = "(&(test_attr1=attr1)(&(test_attr2=attr2)(test_attr3=attr3)))"; + celix_filter_t* filter = celix_filter_create(str); celix_properties_t* props = celix_properties_create(); auto* key = "test_attr1"; auto* val = "attr1"; @@ -386,29 +396,28 @@ TEST_F(FilterTestSuite, match_false) { bool result = celix_filter_match(filter, props); ASSERT_FALSE(result); - //cleanup + // cleanup celix_properties_destroy(props); celix_filter_destroy(filter); } -TEST_F(FilterTestSuite, getString) { - auto* filter_str = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3)))"; - celix_filter_t * filter = celix_filter_create(filter_str); +TEST_F(FilterTestSuite, GetStringTest) { + auto* str = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3)))"; + celix_filter_t* filter = celix_filter_create(str); - const char * get_str = celix_filter_getFilterString(filter); - ASSERT_STREQ(filter_str, get_str); + const char* get_str = celix_filter_getFilterString(filter); + ASSERT_STREQ(str, get_str); - //cleanup + // cleanup celix_filter_destroy(filter); } - -TEST_F(FilterTestSuite, filterMatch) { +TEST_F(FilterTestSuite, FilterEqualsTest) { auto* f1 = celix_filter_create("(test_attr1=attr1)"); auto* f2 = celix_filter_create("(test_attr1=attr1)"); auto* f3 = celix_filter_create("(test_attr1=attr2)"); - EXPECT_TRUE(celix_filter_matchFilter(f1, f2)); - EXPECT_FALSE(celix_filter_matchFilter(f1, f3)); + EXPECT_TRUE(celix_filter_equals(f1, f2)); + EXPECT_FALSE(celix_filter_equals(f1, f3)); celix_filter_destroy(f1); celix_filter_destroy(f2); celix_filter_destroy(f3); @@ -418,16 +427,56 @@ TEST_F(FilterTestSuite, AutoCleanupTest) { celix_autoptr(celix_filter_t) filter = celix_filter_create("(test_attr1=attr1)"); } -TEST_F(FilterTestSuite, create_fail_missing_filter_operand) { - celix_filter_t * filter= celix_filter_create("(&(test))"); - ASSERT_TRUE(filter == NULL); +TEST_F(FilterTestSuite, MissingOperandCreateTest) { + celix_filter_t* filter = celix_filter_create("(&(test))"); + ASSERT_TRUE(filter == nullptr); + + filter = celix_filter_create("(!(test))"); + ASSERT_TRUE(filter == nullptr); +} + +TEST_F(FilterTestSuite, SubStringTest) { + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_set(props, "test", "John Bob Doe"); + + //test filter with matching subInitial + celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(test=Jo*)"); + EXPECT_TRUE(celix_filter_match(filter1, props)); + + //test filter with un-matching subInitial + celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(test=Joo*)"); + EXPECT_FALSE(celix_filter_match(filter2, props)); + + //test filter with matching subFinal + celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(test=*Doe)"); + EXPECT_TRUE(celix_filter_match(filter3, props)); + + //test filter with un-matching subFinal + celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(test=*Doo)"); + EXPECT_FALSE(celix_filter_match(filter4, props)); + + //test filter with matching subAny + celix_autoptr(celix_filter_t) filter5 = celix_filter_create("(test=*Bob*)"); + EXPECT_TRUE(celix_filter_match(filter5, props)); + + //test filter with un-matching subAny + //TODO fixme +// celix_autoptr(celix_filter_t) filter6 = celix_filter_create("(test=*Boo*)"); +// EXPECT_FALSE(celix_filter_match(filter6, props)); + + //test filter with matching subAny, subInitial and subFinal + celix_autoptr(celix_filter_t) filter7 = celix_filter_create("(test=Jo*Bob*Doe)"); + EXPECT_TRUE(celix_filter_match(filter7, props)); + + //test filter with un-matching subAny, subInitial and subFinal + celix_autoptr(celix_filter_t) filter8 = celix_filter_create("(test=Jo*Boo*Doe)"); - filter= celix_filter_create("(!(test))"); - ASSERT_TRUE(filter == NULL); + //test filter with un-matching overlapping subAny, subInitial and subFinal + celix_autoptr(celix_filter_t) filter9 = celix_filter_create("(test=John B*Bob*b Doe)"); } #include "filter.h" -TEST_F(FilterTestSuite, deprecatedApi) { +TEST_F(FilterTestSuite, DeprecatedApiTest) { auto* f1 = filter_create("(test_attr1=attr1)"); auto* f2 = filter_create("(test_attr1=attr1)"); bool result; diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h index 24287a8f..33f24ed2 100644 --- a/libs/utils/include/celix_filter.h +++ b/libs/utils/include/celix_filter.h @@ -62,6 +62,14 @@ struct celix_filter_struct { celix_filter_internal_t* internal; //for internal use only }; +/** + * @brief Create a filter based on the provided filter string. + * + * If the return status is NULL, an error message is logged to celix_err. + * + * @param filterStr The filter string. + * @return The created filter or NULL if the filter string is invalid. + */ CELIX_UTILS_EXPORT celix_filter_t* celix_filter_create(const char *filterStr); CELIX_UTILS_EXPORT void celix_filter_destroy(celix_filter_t *filter); @@ -70,7 +78,7 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_t, celix_filter_destroy) CELIX_UTILS_EXPORT bool celix_filter_match(const celix_filter_t *filter, const celix_properties_t* props); -CELIX_UTILS_EXPORT bool celix_filter_matchFilter(const celix_filter_t *filter1, const celix_filter_t *filter2); +CELIX_UTILS_EXPORT bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *filter2); CELIX_UTILS_EXPORT const char* celix_filter_getFilterString(const celix_filter_t *filter); diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c index a414ebfe..f8eced27 100644 --- a/libs/utils/src/filter.c +++ b/libs/utils/src/filter.c @@ -24,11 +24,13 @@ #include <assert.h> #include <utils.h> -#include "celix_filter.h" -#include "filter.h" +#include "celix_convert_utils.h" +#include "celix_err.h" #include "celix_errno.h" +#include "celix_filter.h" +#include "celix_stdlib_cleanup.h" #include "celix_version.h" -#include "celix_convert_utils.h" +#include "filter.h" struct celix_filter_internal { bool convertedToLong; @@ -65,33 +67,29 @@ void filter_destroy(celix_filter_t * filter) { } static celix_filter_t * filter_parseFilter(char* filterString, int* pos) { - celix_filter_t * filter; filter_skipWhiteSpace(filterString, pos); if (filterString[*pos] == '\0') { //empty filter - fprintf(stderr, "Filter Error: Cannot create LDAP filter from an empty string.\n"); + celix_err_push("Filter Error: Cannot create LDAP filter from an empty string.\n"); return NULL; } else if (filterString[*pos] != '(') { - fprintf(stderr, "Filter Error: Missing '(' in filter string '%s'.\n", filterString); + celix_err_pushf("Filter Error: Missing '(' in filter string '%s'.\n", filterString); return NULL; } (*pos)++; //eat '(' - filter = filter_parseFilterComp(filterString, pos); + celix_autoptr(celix_filter_t) filter = filter_parseFilterComp(filterString, pos); filter_skipWhiteSpace(filterString, pos); if (filterString[*pos] != ')') { - fprintf(stderr, "Filter Error: Missing ')' in filter string '%s'.\n", filterString); - if(filter!=NULL){ - filter_destroy(filter); - } + celix_err_pushf("Filter Error: Missing ')' in filter string '%s'.\n", filterString); return NULL; } (*pos)++; //eat ')' filter_skipWhiteSpace(filterString, pos); - return filter; + return celix_steal_ptr(filter); } static celix_filter_t * filter_parseFilterComp(char * filterString, int * pos) { @@ -516,7 +514,7 @@ static int celix_filter_compareAttributeValue(const celix_filter_t* filter, cons return strcmp(propertyValue, filter->value); } -static celix_status_t filter_compareSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry, bool* out) { +static bool filter_compareSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) { const char* propertyValue = entry->value; int pos = 0; int size = celix_arrayList_size(filter->children); @@ -532,8 +530,7 @@ static celix_status_t filter_compareSubString(const celix_filter_t* filter, cons } index = strcspn(propertyValue+pos, substr2); if (index == strlen(propertyValue+pos)) { - *out = false; - return CELIX_SUCCESS; + return false; } pos = index + strlen(substr2); @@ -542,15 +539,14 @@ static celix_status_t filter_compareSubString(const celix_filter_t* filter, cons } } else { unsigned int len = strlen(substr); - char * region = (char *)calloc(1, len+1); + char * region = (char *)calloc(1, len+1); //TODO refactor filter compile to prevent calloc need strncpy(region, propertyValue+pos, len); region[len] = '\0'; if (strcmp(region, substr) == 0) { pos += len; } else { free(region); - *out = false; - return CELIX_SUCCESS; + return false; } free(region); } @@ -559,62 +555,44 @@ static celix_status_t filter_compareSubString(const celix_filter_t* filter, cons int begin; if (substr == NULL) { - *out = true; - return CELIX_SUCCESS; + return true; } len = strlen(substr); begin = strlen(propertyValue)-len; - *out = (strcmp(propertyValue+begin, substr) == 0); - return CELIX_SUCCESS; + return (strcmp(propertyValue+begin, substr) == 0); } } - *out = true; - return CELIX_SUCCESS; + return true; } -static celix_status_t filter_matchPropertyEntry(const celix_filter_t* filter, const celix_properties_entry_t* entry, bool *out) { - celix_status_t status = CELIX_SUCCESS; - bool result = false; - +static bool celix_filter_matchPropertyEntry(const celix_filter_t* filter, const celix_properties_entry_t* entry) { if (filter == NULL || entry == NULL) { - *out = false; - return status; + return false; } switch (filter->operand) { case CELIX_FILTER_OPERAND_SUBSTRING: - return filter_compareSubString(filter, entry, out); + return filter_compareSubString(filter, entry); case CELIX_FILTER_OPERAND_APPROX: - *out = strcasecmp(entry->value, filter->value) == 0; - return CELIX_SUCCESS; + return strcasecmp(entry->value, filter->value) == 0; case CELIX_FILTER_OPERAND_EQUAL: - *out = (celix_filter_compareAttributeValue(filter, entry) == 0); - return CELIX_SUCCESS; + return celix_filter_compareAttributeValue(filter, entry) == 0; case CELIX_FILTER_OPERAND_GREATER: - *out = (celix_filter_compareAttributeValue(filter, entry) > 0); - return CELIX_SUCCESS; + return celix_filter_compareAttributeValue(filter, entry) > 0; case CELIX_FILTER_OPERAND_GREATEREQUAL: - *out = (celix_filter_compareAttributeValue(filter, entry) >= 0); - return CELIX_SUCCESS; + return celix_filter_compareAttributeValue(filter, entry) >= 0; case CELIX_FILTER_OPERAND_LESS: - *out = (celix_filter_compareAttributeValue(filter, entry) < 0); - return CELIX_SUCCESS; + return celix_filter_compareAttributeValue(filter, entry) < 0; case CELIX_FILTER_OPERAND_LESSEQUAL: - *out = (celix_filter_compareAttributeValue(filter, entry) <= 0); - return CELIX_SUCCESS; + return celix_filter_compareAttributeValue(filter, entry) <= 0; case CELIX_FILTER_OPERAND_AND: case CELIX_FILTER_OPERAND_NOT: case CELIX_FILTER_OPERAND_OR: case CELIX_FILTER_OPERAND_PRESENT: default: - ; - /* fall through */ + assert(false); //should not reach here } - - if (out != NULL) { - *out = result; - } - return status; + return false; } celix_status_t filter_getString(celix_filter_t * filter, const char **filterStr) { @@ -625,7 +603,7 @@ celix_status_t filter_getString(celix_filter_t * filter, const char **filterStr) } celix_status_t filter_match_filter(celix_filter_t *src, celix_filter_t *dest, bool *out) { - bool result = celix_filter_matchFilter(src, dest); + bool result = celix_filter_equals(src, dest); if (out != NULL) { *out = result; } @@ -634,186 +612,188 @@ celix_status_t filter_match_filter(celix_filter_t *src, celix_filter_t *dest, bo celix_filter_t* celix_filter_create(const char *filterString) { - celix_filter_t * filter = NULL; - char* filterStr = string_ndup(filterString, 1024*1024); + celix_autofree char* str = celix_utils_strdup(filterString); + if (!str) { + celix_err_push("Failed to create filter string"); + // TODO test error + return NULL; + } + int pos = 0; - filter = filter_parseFilter(filterStr, &pos); - if (filter != NULL && pos != strlen(filterStr)) { - fprintf(stderr, "Filter Error: Extraneous trailing characters.\n"); - filter_destroy(filter); - filter = NULL; - } else if (filter != NULL) { - if (filter->operand != CELIX_FILTER_OPERAND_OR && filter->operand != CELIX_FILTER_OPERAND_AND && - filter->operand != CELIX_FILTER_OPERAND_NOT && filter->operand != CELIX_FILTER_OPERAND_SUBSTRING && - filter->operand != CELIX_FILTER_OPERAND_PRESENT) { - if (filter->attribute == NULL || filter->value == NULL) { - filter_destroy(filter); - filter = NULL; - } - } + celix_autoptr(celix_filter_t) filter = filter_parseFilter(str, &pos); + if (!filter) { + celix_err_push("Failed to parse filter string"); + return NULL; + } + if (pos != strlen(filterString)) { + celix_err_push("Filter Error: Extraneous trailing characters.\n"); + return NULL; } - if (filter == NULL) { - free(filterStr); - } else { - filter->filterStr = filterStr; - celix_status_t status = celix_filter_compile(filter); - if (status != CELIX_SUCCESS) { - celix_filter_destroy(filter); - filter = NULL; + if (filter->operand != CELIX_FILTER_OPERAND_OR && filter->operand != CELIX_FILTER_OPERAND_AND && + filter->operand != CELIX_FILTER_OPERAND_NOT && filter->operand != CELIX_FILTER_OPERAND_SUBSTRING && + filter->operand != CELIX_FILTER_OPERAND_PRESENT) { + if (filter->attribute == NULL || filter->value == NULL) { + celix_err_push("Filter Error: Missing attribute or value.\n"); + return NULL; } } - return filter; + filter->filterStr = celix_steal_ptr(str); + if (celix_filter_compile(filter) != CELIX_SUCCESS) { + // TODO test error + return NULL; + } + return celix_steal_ptr(filter); } -void celix_filter_destroy(celix_filter_t *filter) { - if (filter != NULL) { - if(filter->children != NULL) { - if (filter->operand == CELIX_FILTER_OPERAND_SUBSTRING) { - int size = celix_arrayList_size(filter->children); - int i = 0; - for (i = 0; i < size; i++) { - char *operand = celix_arrayList_get(filter->children, i); - free(operand); - } - celix_arrayList_destroy(filter->children); - filter->children = NULL; - } else if (filter->operand == CELIX_FILTER_OPERAND_OR || filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_NOT) { - int size = celix_arrayList_size(filter->children); - int i = 0; - for (i = 0; i < size; i++) { - celix_filter_t *f = celix_arrayList_get(filter->children, i); - celix_filter_destroy(f); - } - celix_arrayList_destroy(filter->children); - filter->children = NULL; - } else { - fprintf(stderr, "Filter Error: Corrupt filter. children has a value, but not an expected operand\n"); - } - } - free((char*)filter->value); - filter->value = NULL; - free((char*)filter->attribute); - filter->attribute = NULL; - free((char*)filter->filterStr); - filter->filterStr = NULL; - if (filter->internal != NULL) { - celix_version_destroy(filter->internal->versionValue); - free(filter->internal); - } - free(filter); - } +void celix_filter_destroy(celix_filter_t* filter) { + if (!filter) { + return; + } + + if (filter->children != NULL) { + int size = celix_arrayList_size(filter->children); + if (filter->operand == CELIX_FILTER_OPERAND_SUBSTRING) { + for (int i = 0; i < size; i++) { + char* operand = celix_arrayList_get(filter->children, i); + free(operand); + } + celix_arrayList_destroy(filter->children); + filter->children = NULL; + } else if (filter->operand == CELIX_FILTER_OPERAND_OR || filter->operand == CELIX_FILTER_OPERAND_AND || + filter->operand == CELIX_FILTER_OPERAND_NOT) { + for (int i = 0; i < size; i++) { + celix_filter_t* f = celix_arrayList_get(filter->children, i); + celix_filter_destroy(f); + } + celix_arrayList_destroy(filter->children); + filter->children = NULL; + } + } + free((char*)filter->value); + filter->value = NULL; + free((char*)filter->attribute); + filter->attribute = NULL; + free((char*)filter->filterStr); + filter->filterStr = NULL; + if (filter->internal != NULL) { + celix_version_destroy(filter->internal->versionValue); + free(filter->internal); + } + free(filter); } -bool celix_filter_match(const celix_filter_t *filter, const celix_properties_t* properties) { +bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t* properties) { if (filter == NULL) { - return true; //matching on null(empty) filter is always true + return true; // matching on null(empty) filter is always true } + switch (filter->operand) { - case CELIX_FILTER_OPERAND_AND: { - celix_array_list_t* children = filter->children; - for (int i = 0; i < celix_arrayList_size(children); i++) { - celix_filter_t * sfilter = (celix_filter_t *) celix_arrayList_get(children, i); - bool mresult = celix_filter_match(sfilter, properties); - if (!mresult) { - return false; - } - } - return true; - } - case CELIX_FILTER_OPERAND_OR: { - celix_array_list_t* children = filter->children; - for (int i = 0; i < celix_arrayList_size(children); i++) { - celix_filter_t * sfilter = (celix_filter_t *) celix_arrayList_get(children, i); - bool mresult = celix_filter_match(sfilter, properties); - if (mresult) { - return true; - } + case CELIX_FILTER_OPERAND_AND: { + celix_array_list_t* childern = filter->children; + for (int i = 0; i < celix_arrayList_size(childern); i++) { + celix_filter_t* childFilter = (celix_filter_t*)celix_arrayList_get(childern, i); + bool childResult = celix_filter_match(childFilter, properties); + if (!childResult) { + return false; } - return false; } - case CELIX_FILTER_OPERAND_NOT: { - celix_filter_t * sfilter = celix_arrayList_get(filter->children, 0); - bool mresult = celix_filter_match(sfilter, properties); - return !mresult; - } - case CELIX_FILTER_OPERAND_PRESENT: { - char * value = (properties == NULL) ? NULL: (char*)celix_properties_get(properties, filter->attribute, NULL); - return value != NULL; - } - case CELIX_FILTER_OPERAND_SUBSTRING : - case CELIX_FILTER_OPERAND_EQUAL : - case CELIX_FILTER_OPERAND_GREATER : - case CELIX_FILTER_OPERAND_GREATEREQUAL : - case CELIX_FILTER_OPERAND_LESS : - case CELIX_FILTER_OPERAND_LESSEQUAL : - case CELIX_FILTER_OPERAND_APPROX : { - celix_properties_entry_t *entry = celix_properties_getEntry(properties, filter->attribute); - bool result; - celix_status_t status = filter_matchPropertyEntry(filter, entry, &result); - if (status != CELIX_SUCCESS) { - //TODO handle error - return false; + return true; + } + case CELIX_FILTER_OPERAND_OR: { + celix_array_list_t* children = filter->children; + for (int i = 0; i < celix_arrayList_size(children); i++) { + celix_filter_t* childFilter = (celix_filter_t*)celix_arrayList_get(children, i); + bool childResult = celix_filter_match(childFilter, properties); + if (childResult) { + return true; } - return result; } + return false; + } + case CELIX_FILTER_OPERAND_NOT: { + celix_filter_t* childFilter = celix_arrayList_get(filter->children, 0); + bool childResult = celix_filter_match(childFilter, properties); + return !childResult; + } + case CELIX_FILTER_OPERAND_PRESENT: { + char* value = (properties == NULL) ? NULL : (char*)celix_properties_get(properties, filter->attribute, NULL); + return value != NULL; + } + case CELIX_FILTER_OPERAND_SUBSTRING: + case CELIX_FILTER_OPERAND_EQUAL: + case CELIX_FILTER_OPERAND_GREATER: + case CELIX_FILTER_OPERAND_GREATEREQUAL: + case CELIX_FILTER_OPERAND_LESS: + case CELIX_FILTER_OPERAND_LESSEQUAL: + case CELIX_FILTER_OPERAND_APPROX: { + const celix_properties_entry_t* entry = celix_properties_getEntry(properties, filter->attribute); + bool result = celix_filter_matchPropertyEntry(filter, entry); + return result; + } } - //LCOV_EXCL_START - assert(false); //should never happen + // LCOV_EXCL_START + assert(false); // should never happen return false; - //LCOV_EXCL_STOP + // LCOV_EXCL_STOP } -bool celix_filter_matchFilter(const celix_filter_t *filter1, const celix_filter_t *filter2) { - bool result = false; +bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *filter2) { if (filter1 == filter2) { - result = true; //NOTE. also means NULL filter are equal - } else if (filter1 != NULL && filter2 != NULL && filter1->operand == filter2->operand) { - if (filter1->operand == CELIX_FILTER_OPERAND_AND || filter1->operand == CELIX_FILTER_OPERAND_OR || filter1->operand == CELIX_FILTER_OPERAND_NOT) { - assert(filter1->children != NULL); - assert(filter2->children != NULL); - int sizeSrc = celix_arrayList_size(filter1->children); - int sizeDest = celix_arrayList_size(filter2->children); - if (sizeSrc == sizeDest) { - int i; - int k; - int sameCount = 0; - for (i =0; i < sizeSrc; ++i) { - bool same = false; - celix_filter_t *srcPart = celix_arrayList_get(filter1->children, i); - for (k = 0; k < sizeDest; ++k) { - celix_filter_t *destPart = celix_arrayList_get(filter2->children, k); - filter_match_filter(srcPart, destPart, &same); - if (same) { - sameCount += 1; - break; - } + return true; + } + if (!filter1 || !filter2) { + return false; + } + if (filter1->operand != filter2->operand) { + return false; + } + + + if (filter1->operand == CELIX_FILTER_OPERAND_AND || filter1->operand == CELIX_FILTER_OPERAND_OR || filter1->operand == CELIX_FILTER_OPERAND_NOT) { + assert(filter1->children != NULL); + assert(filter2->children != NULL); + int sizeSrc = celix_arrayList_size(filter1->children); + int sizeDest = celix_arrayList_size(filter2->children); + if (sizeSrc == sizeDest) { + int i; + int k; + int sameCount = 0; + for (i =0; i < sizeSrc; ++i) { + bool same = false; + celix_filter_t *srcPart = celix_arrayList_get(filter1->children, i); + for (k = 0; k < sizeDest; ++k) { + celix_filter_t *destPart = celix_arrayList_get(filter2->children, k); + filter_match_filter(srcPart, destPart, &same); + if (same) { + sameCount += 1; + break; } } - result = sameCount == sizeSrc; - } - } else { //compare attr and value - bool attrSame = false; - bool valSame = false; - if (filter1->attribute == NULL && filter2->attribute == NULL) { - attrSame = true; - } else if (filter1->attribute != NULL && filter2->attribute != NULL) { - attrSame = celix_utils_stringEquals(filter1->attribute, filter2->attribute); } + return sameCount == sizeSrc; + } + return false; + } - if (filter1->value == NULL && filter2->value == NULL) { - valSame = true; - } else if (filter1->value != NULL && filter2->value != NULL) { - valSame = celix_utils_stringEquals(filter1->value, filter2->value); - } + //compare attr and value + bool attrSame = false; + bool valSame = false; + if (filter1->attribute == NULL && filter2->attribute == NULL) { + attrSame = true; + } else if (filter1->attribute != NULL && filter2->attribute != NULL) { + attrSame = celix_utils_stringEquals(filter1->attribute, filter2->attribute); + } - result = attrSame && valSame; - } + if (filter1->value == NULL && filter2->value == NULL) { + valSame = true; + } else if (filter1->value != NULL && filter2->value != NULL) { + valSame = celix_utils_stringEquals(filter1->value, filter2->value); } - return result; + return attrSame && valSame; } const char* celix_filter_getFilterString(const celix_filter_t *filter) { @@ -907,4 +887,4 @@ static bool hasMandatoryNegatedPresenceAttribute(const celix_filter_t *filter, c } bool celix_filter_hasMandatoryNegatedPresenceAttribute(const celix_filter_t *filter, const char *attribute) { return hasMandatoryNegatedPresenceAttribute(filter, attribute, false, false); -} \ No newline at end of file +}
