You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2023/11/28 21:45:22 UTC
(celix) 09/09: Refactor filter to use typed properties, improve quality and test coverage
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 09effe20b9b4d7a432f10624c19f3e744ea3b662
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Tue Nov 28 22:36:24 2023 +0100
Refactor filter to use typed properties, improve quality and test coverage
---
libs/utils/gtest/CMakeLists.txt | 2 +
libs/utils/gtest/src/CxxFilterTestSuite.cc | 7 +-
.../gtest/src/FilterErrorInjectionTestSuite.cc | 165 ++++
libs/utils/gtest/src/FilterTestSuite.cc | 78 +-
libs/utils/include/celix/Filter.h | 19 +-
libs/utils/include/celix_filter.h | 4 +-
libs/utils/src/filter.c | 911 +++++++++++----------
7 files changed, 736 insertions(+), 450 deletions(-)
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index bfb9a553..8b6381dc 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -99,6 +99,7 @@ if (EI_TESTS)
src/PropertiesErrorInjectionTestSuite.cc
src/VersionErrorInjectionTestSuite.cc
src/HashMapErrorInjectionTestSuite.cc
+ src/FilterErrorInjectionTestSuite.cc
)
target_link_libraries(test_utils_with_ei PRIVATE
Celix::zip_ei
@@ -114,6 +115,7 @@ if (EI_TESTS)
Celix::string_hash_map_ei
Celix::long_hash_map_ei
Celix::version_ei
+ Celix::array_list_ei
GTest::gtest GTest::gtest_main
)
target_include_directories(test_utils_with_ei PRIVATE ../src) #for version_private (needs refactoring of test)
diff --git a/libs/utils/gtest/src/CxxFilterTestSuite.cc b/libs/utils/gtest/src/CxxFilterTestSuite.cc
index 0389d4b6..549eabba 100644
--- a/libs/utils/gtest/src/CxxFilterTestSuite.cc
+++ b/libs/utils/gtest/src/CxxFilterTestSuite.cc
@@ -28,15 +28,10 @@ public:
TEST_F(CxxFilterTestSuite, CreateDestroy) {
celix::Filter filter{};
- EXPECT_TRUE(filter.empty());
- EXPECT_TRUE(filter.getCFilter() == nullptr);
- EXPECT_TRUE(filter.getFilterString().empty());
+ EXPECT_EQ(filter.getFilterString(), "(|)"); //match all filter
}
TEST_F(CxxFilterTestSuite, FilterString) {
- celix::Filter filter1{};
- EXPECT_EQ(std::string{}, filter1.getFilterString());
-
celix::Filter filter2{"(key=value)"};
EXPECT_FALSE(filter2.empty());
EXPECT_EQ(std::string{"(key=value)"}, filter2.getFilterString());
diff --git a/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc b/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc
new file mode 100644
index 00000000..cf676708
--- /dev/null
+++ b/libs/utils/gtest/src/FilterErrorInjectionTestSuite.cc
@@ -0,0 +1,165 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "celix_filter.h"
+#include "celix_utils.h"
+#include "celix_err.h"
+
+#include "celix_array_list_ei.h"
+#include "malloc_ei.h"
+#include "stdio_ei.h"
+
+class FilterErrorInjectionTestSuite : public ::testing::Test {
+ public:
+ FilterErrorInjectionTestSuite() {
+ celix_err_resetErrors();
+ celix_ei_expect_celix_arrayList_create(nullptr, 0, nullptr);
+ celix_ei_expect_calloc(nullptr, 0, nullptr);
+ celix_ei_expect_open_memstream(nullptr, 0, nullptr);
+ celix_ei_expect_fclose(nullptr, 0, 0);
+ }
+
+ ~FilterErrorInjectionTestSuite() override {
+ celix_err_printErrors(stderr, nullptr, nullptr);
+ }
+};
+
+TEST_F(FilterErrorInjectionTestSuite, ErrorWithArrayListCreateTest) {
+ //Given an error injection for celix_arrayList_create
+ celix_ei_expect_celix_arrayList_create((void*)celix_filter_create, 3, nullptr);
+ //When creating a filter with a AND filter node
+ const char* filterStr = "(&(key1=value1)(key2=value2))";
+ //Then the filter creation should fail, because it cannot create a children list for the AND filter node
+ celix_filter_t* filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for celix_arrayList_create
+ celix_ei_expect_celix_arrayList_create((void*)celix_filter_create, 3, nullptr);
+ //When creating a filter with a NOT filter node
+ filterStr = "(!(key1=value1))";
+ //Then the filter creation should fail, because it cannot create a children list for the NOT filter node
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for celix_arrayList_create
+ celix_ei_expect_celix_arrayList_create((void*)celix_filter_create, 4, nullptr);
+ //When creating a filter with a substring
+ filterStr = "(key1=*val*)";
+ //Then the filter creation should fail, because it cannot create a children list for the substring filter node
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+}
+
+TEST_F(FilterErrorInjectionTestSuite, ErrorWithCallocTest) {
+ //Given an error injection for calloc
+ celix_ei_expect_calloc((void*)celix_filter_create, 3, nullptr);
+ //When creating a filter with a AND filter node
+ const char* filterStr = "(&(key1=value1)(key2=value2))";
+ //Then the filter creation should fail, because it cannot calloc mem for a filter node
+ celix_filter_t* filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for calloc
+ celix_ei_expect_calloc((void*)celix_filter_create, 3, nullptr);
+ //When creating a filter with a NOT filter node
+ filterStr = "(!(key1=value1))";
+ //Then the filter creation should fail, because it cannot calloc mem for a filter node
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for calloc
+ celix_ei_expect_calloc((void*)celix_filter_create, 3, nullptr);
+ //When creating a filter with a no compound filter node
+ filterStr = "(key1=value1)";
+ //Then the filter creation should fail, because it cannot calloc mem for a filter node
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for calloc
+ celix_ei_expect_calloc((void*)celix_filter_create, 1, nullptr);
+ //When creating a filter with
+ filterStr = "(key1=value1)";
+ //Then the filter creation should fail, because it cannot calloc mem for an internal struct (converted types)
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+}
+
+TEST_F(FilterErrorInjectionTestSuite, ErrorMemStreamTest) {
+ //Given an error injection for open_memstream
+ celix_ei_expect_open_memstream((void*)celix_filter_create, 4, nullptr);
+ //When creating a filter with a parseable attribute (or attr value)
+ const char* filterStr = "(key1=value1)";
+ //Then the filter creation should fail, because it cannot open a memstream to create the attribute string.
+ celix_filter_t* filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for open_memstream
+ celix_ei_expect_open_memstream((void*)celix_filter_create, 5, nullptr);
+ //When creating a filter with a sub string with an any part
+ filterStr = "(key1=*val*)";
+ //Then the filter creation should fail, because it cannot open a memstream to create an any part
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+}
+
+TEST_F(FilterErrorInjectionTestSuite, ErrorFcloseTest) {
+ //Given an error injection for fclose
+ celix_ei_expect_fclose((void*)celix_filter_create, 4, EOF);
+ //When creating a filter with a parseable attribute (or attr value)
+ const char* filterStr = "(key1=value1)";
+ //Then the filter creation should fail, because it cannot close the (mem)stream.
+ celix_filter_t* filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for fclose
+ celix_ei_expect_fclose((void*)celix_filter_create, 5, EOF);
+ //When creating a filter with a sub string with an any part
+ filterStr = "(key1=*val*)";
+ //Then the filter creation should fail, because it cannot close the (mem)stream.
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+}
+
+TEST_F(FilterErrorInjectionTestSuite, ErrorFputcTest) {
+ // Given an error injection for fputc
+ celix_ei_expect_fputc((void*)celix_filter_create, 4, EOF);
+ // When creating a filter with a parseable attribute (or attr value)
+ const char* filterStr = "(k=value1)";
+ // Then the filter creation should fail, because it cannot fputc a char to the (mem)stream.
+ celix_filter_t* filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ // Given an error injection for fputc with ordinal 2
+ celix_ei_expect_fputc((void*)celix_filter_create, 4, EOF, 2);
+ // When creating a filter with a parseable attribute (or attr value)
+ filterStr = "(k=value1)";
+ // Then the filter creation should fail, because it cannot fputc a '\0' char to the (mem)stream.
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+
+ //Given an error injection for fclose
+ celix_ei_expect_fputc((void*)celix_filter_create, 5, EOF);
+ //When creating a filter with a sub string with an any part
+ filterStr = "(key1=*val*)";
+ //Then the filter creation should fail, because it cannot close the (mem)stream.
+ filter = celix_filter_create(filterStr);
+ EXPECT_EQ(nullptr, filter);
+}
diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc
index 0a493f73..e571349c 100644
--- a/libs/utils/gtest/src/FilterTestSuite.cc
+++ b/libs/utils/gtest/src/FilterTestSuite.cc
@@ -85,20 +85,19 @@ TEST_F(FilterTestSuite, MissingClosingBracketsCreateTest) {
}
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);
+ filter = celix_filter_create("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=at(tr3)))");
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);
+ filter = celix_filter_create("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=att(r3)))");
+ ASSERT_TRUE(filter == nullptr);
+
+ // test invalid AND ending
+ filter = celix_filter_create("(&(");
ASSERT_TRUE(filter == nullptr);
- free(str);
}
TEST_F(FilterTestSuite, MiscInvalidCreateTest) {
@@ -131,14 +130,14 @@ TEST_F(FilterTestSuite, MiscInvalidCreateTest) {
filter = celix_filter_create(str5);
ASSERT_TRUE(filter == nullptr);
- // test parsing a value with a escaped closing bracket "\)"
+ // test parsing a value with an escaped closing parenthesis "\ ")"
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 "\)"
+ // test parsing a substring with an escaped closing parenthesis "\"
const char* str7 = "(test_attr3=strWith\\)inIt)";
filter = celix_filter_create(str7);
ASSERT_TRUE(filter != nullptr);
@@ -169,7 +168,7 @@ TEST_F(FilterTestSuite, MatchEqualTest) {
filter = celix_filter_create(str);
ASSERT_TRUE(filter == nullptr);
result = celix_filter_match(filter, props);
- ASSERT_TRUE(result);
+ ASSERT_FALSE(result);
// cleanup
celix_properties_destroy(props);
@@ -465,6 +464,7 @@ TEST_F(FilterTestSuite, TypedPropertiesAndFilterTest) {
TEST_F(FilterTestSuite, SubStringTest) {
celix_autoptr(celix_properties_t) props = celix_properties_create();
celix_properties_set(props, "test", "John Bob Doe");
+ celix_properties_set(props, "test2", "*ValueWithStar");
//test filter with matching subInitial
celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(test=Jo*)");
@@ -487,9 +487,8 @@ TEST_F(FilterTestSuite, SubStringTest) {
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));
+ 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)");
@@ -497,11 +496,62 @@ TEST_F(FilterTestSuite, SubStringTest) {
//test filter with un-matching subAny, subInitial and subFinal
celix_autoptr(celix_filter_t) filter8 = celix_filter_create("(test=Jo*Boo*Doe)");
+ EXPECT_FALSE(celix_filter_match(filter8, props));
- //test filter with un-matching overlapping subAny, subInitial and subFinal
+ //test filter with un-matching overlapping subAny and subInitial
celix_autoptr(celix_filter_t) filter9 = celix_filter_create("(test=John B*Bob*b Doe)");
+ EXPECT_FALSE(celix_filter_match(filter9, props));
+
+ //test filter with un-matching overlapping subAny and subFinal
+ celix_autoptr(celix_filter_t) filter10 = celix_filter_create("(test=*Bob*b Doe)");
+ EXPECT_FALSE(celix_filter_match(filter10, props));
+
+ //test filter with a starting escaped asterisk
+ celix_autoptr(celix_filter_t) filter11 = celix_filter_create("(test2=\\*Value*)");
+ EXPECT_TRUE(celix_filter_match(filter11, props));
+
+ //test filter with an invalid substring
+ celix_autoptr(celix_filter_t) filter12 = celix_filter_create("(test=Bob*");
+ EXPECT_EQ(nullptr, filter12);
}
+TEST_F(FilterTestSuite, CreateEmptyFilter) {
+ celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+ celix_autoptr(celix_filter_t) filter1 = celix_filter_create(nullptr); //fallback to "(|)"
+ EXPECT_TRUE(filter1 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter1, props));
+
+ celix_autoptr(celix_filter_t) filter2 = celix_filter_create(""); //fallback to "(|)"
+ EXPECT_TRUE(filter2 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter2, props));
+
+ celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(|)");
+ EXPECT_TRUE(filter3 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter3, props));
+}
+
+TEST_F(FilterTestSuite, LogicalOperatorsWithNoCriteriaTest) {
+ celix_autoptr(celix_properties_t) props = celix_properties_create();
+
+ celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(&)");
+ EXPECT_TRUE(filter1 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter1, props));
+
+ celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(|)");
+ EXPECT_TRUE(filter2 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter2, props));
+
+ celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(!)"); //NOT with no criteria is not valid
+ EXPECT_TRUE(filter3 == nullptr);
+}
+
+
+TEST_F(FilterTestSuite, InvalidEscapeTest) {
+ EXPECT_EQ(nullptr, celix_filter_create("(\\")); //escape without following char
+}
+
+
#include "filter.h"
TEST_F(FilterTestSuite, DeprecatedApiTest) {
auto* f1 = filter_create("(test_attr1=attr1)");
diff --git a/libs/utils/include/celix/Filter.h b/libs/utils/include/celix/Filter.h
index 97c10159..b9789ae5 100644
--- a/libs/utils/include/celix/Filter.h
+++ b/libs/utils/include/celix/Filter.h
@@ -23,6 +23,7 @@
#include <memory>
#include "celix_filter.h"
+#include "celix_err.h"
#include "celix/Exception.h"
#include "celix/Properties.h"
@@ -50,7 +51,18 @@ namespace celix {
*/
class Filter {
public:
- Filter() : cFilter{createFilter("")} {}
+ /**
+ * @brief Create an empty ("()") filter based on the provided filter string.
+ * @param[in] filterStr The filter string.
+ * @throw celix::FilterException if the filter string is invalid also logs an error message is to celix_err.
+ */
+ Filter() : cFilter{createFilter(nullptr)} {}
+
+ /**
+ * @brief Create a filter based on the provided filter string.
+ * @param[in] filterStr The filter string.
+ * @throw celix::FilterException if the filter string is invalid also logs an error message is to celix_err.
+ */
explicit Filter(const std::string& filterStr) : cFilter{createFilter(filterStr.c_str())} {}
@@ -157,12 +169,9 @@ namespace celix {
private:
static std::shared_ptr<celix_filter_t> createFilter(const char* filterStr) {
- if (filterStr == nullptr || strnlen(filterStr, 1) == 0) {
- return nullptr;
- }
auto* cf = celix_filter_create(filterStr);
if (cf == nullptr) {
- throw celix::FilterException{"Invalid LDAP filter '" + std::string{filterStr} + "'"};
+ throw celix::FilterException{"Cannot create filter: '" + std::string{filterStr} + "'"};
}
return std::shared_ptr<celix_filter_t>{cf, [](celix_filter_t *f) {
celix_filter_destroy(f);
diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h
index c8b0d7e0..1cdb8eb7 100644
--- a/libs/utils/include/celix_filter.h
+++ b/libs/utils/include/celix_filter.h
@@ -114,7 +114,7 @@ typedef struct celix_filter_struct {
*
* If the return status is NULL, an error message is logged to celix_err.
*
- * @param filterStr The filter string.
+ * @param filterStr The filter string. If NULL or "" a match-all "(|)" filter is created.
* @return The created filter or NULL if the filter string is invalid.
*/
CELIX_UTILS_EXPORT celix_filter_t* celix_filter_create(const char* filterStr);
@@ -133,7 +133,7 @@ CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_t, celix_filter_destroy)
* @brief Check whether the provided filter matches the provided properties.
* @param[in] filter The filter.
* @param[in] props The properties.
- * @return True if the filter matches the properties, false otherwise.
+ * @return True if the filter matches the properties, false otherwise. If filter or props is NULL false is returned.
*/
CELIX_UTILS_EXPORT bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t* props);
diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c
index e1a20652..fafc1ee6 100644
--- a/libs/utils/src/filter.c
+++ b/libs/utils/src/filter.c
@@ -17,23 +17,24 @@
* under the License.
*/
+#include <assert.h>
+#include <ctype.h>
#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
-#include <ctype.h>
-#include <assert.h>
+#include <string.h>
#include <utils.h>
#include "celix_convert_utils.h"
#include "celix_err.h"
#include "celix_errno.h"
#include "celix_filter.h"
+#include "celix_stdio_cleanup.h"
#include "celix_stdlib_cleanup.h"
#include "celix_version.h"
#include "filter.h"
-//ignoring clang-tidy recursion warnings for this file, because filter uses recursion
-//NOLINTBEGIN(misc-no-recursion)
+// ignoring clang-tidy recursion warnings for this file, because filter uses recursion
+// NOLINTBEGIN(misc-no-recursion)
struct celix_filter_internal {
bool convertedToLong;
@@ -46,396 +47,486 @@ struct celix_filter_internal {
bool boolValue;
bool convertedToVersion;
- celix_version_t *versionValue;
+ celix_version_t* versionValue;
};
-static void filter_skipWhiteSpace(char* filterString, int* pos);
-static celix_filter_t * filter_parseFilter(char* filterString, int* pos);
-static celix_filter_t * filter_parseFilterComp(char* filterString, int* pos);
-static celix_filter_t * filter_parseAndOrOr(char* filterString, celix_filter_operand_t andOrOr, int* pos);
-static celix_filter_t * filter_parseNot(char* filterString, int* pos);
-static celix_filter_t * filter_parseItem(char* filterString, int* pos);
-static char * filter_parseAttr(char* filterString, int* pos);
-static char * filter_parseValue(char* filterString, int* pos);
-static celix_array_list_t* filter_parseSubstring(char* filterString, int* pos);
-
-static void filter_skipWhiteSpace(char* filterString, int* pos) {
+static void celix_filter_skipWhiteSpace(char* filterString, int* pos);
+static celix_filter_t* celix_filter_parseFilter(char* filterString, int* pos);
+static celix_filter_t* celix_filter_parseFilterNode(char* filterString, int* pos);
+static celix_filter_t* celix_filter_parseAndOrOr(char* filterString, celix_filter_operand_t andOrOr, int* pos);
+static celix_filter_t* celix_filter_parseNot(char* filterString, int* pos);
+static celix_filter_t* celix_filter_parseItem(char* filterString, int* pos);
+static char* celix_filter_parseAttributeOrValue(const char* filterString, int* pos, bool parseAttribute);
+static celix_array_list_t* celix_filter_parseSubstring(char* filterString, int* pos);
+static bool celix_filter_isSubString(const char* filterString, int startPos);
+static bool celix_filter_isNextNonWhiteSpaceChar(const char* filterString, int pos, char c);
+
+static void celix_filter_skipWhiteSpace(char* filterString, int* pos) {
size_t length;
for (length = strlen(filterString); (*pos < length) && isspace(filterString[*pos]);) {
(*pos)++;
}
}
-celix_filter_t * filter_create(const char* filterString) {
- return celix_filter_create(filterString);
+static bool celix_filter_isNextNonWhiteSpaceChar(const char* filterString, int pos, char c) {
+ for (int i = pos; filterString[i] != '\0'; i++) {
+ if (!isspace(filterString[i])) {
+ return filterString[i] == c;
+ }
+ }
+ return false;
}
-void filter_destroy(celix_filter_t * filter) {
- return celix_filter_destroy(filter);
-}
+celix_filter_t* filter_create(const char* filterString) { return celix_filter_create(filterString); }
-static celix_filter_t * filter_parseFilter(char* filterString, int* pos) {
- filter_skipWhiteSpace(filterString, pos);
- if (filterString[*pos] == '\0') {
- //empty filter
- celix_err_push("Filter Error: Cannot create LDAP filter from an empty string.\n");
- return NULL;
- } else if (filterString[*pos] != '(') {
- celix_err_pushf("Filter Error: Missing '(' in filter string '%s'.\n", filterString);
+void filter_destroy(celix_filter_t* filter) { return celix_filter_destroy(filter); }
+
+static celix_filter_t* celix_filter_parseFilter(char* filterString, int* pos) {
+ celix_filter_skipWhiteSpace(filterString, pos);
+ if (filterString[*pos] != '(') {
+ celix_err_pushf("Filter Error: Missing '(' in filter string '%s'.", filterString);
return NULL;
}
- (*pos)++; //eat '('
+ (*pos)++; // eat '('
- celix_autoptr(celix_filter_t) filter = filter_parseFilterComp(filterString, pos);
+ celix_autoptr(celix_filter_t) filter = celix_filter_parseFilterNode(filterString, pos);
- filter_skipWhiteSpace(filterString, pos);
+ celix_filter_skipWhiteSpace(filterString, pos);
if (filterString[*pos] != ')') {
- celix_err_pushf("Filter Error: Missing ')' in filter string '%s'.\n", filterString);
+ celix_err_pushf("Filter Error: Missing ')' in filter string '%s'.", filterString);
return NULL;
}
- (*pos)++; //eat ')'
- filter_skipWhiteSpace(filterString, pos);
+ (*pos)++; // eat ')'
+ celix_filter_skipWhiteSpace(filterString, pos);
return celix_steal_ptr(filter);
}
-static celix_filter_t * filter_parseFilterComp(char * filterString, int * pos) {
+static celix_filter_t* celix_filter_parseFilterNode(char* filterString, int* pos) {
char c;
- filter_skipWhiteSpace(filterString, pos);
+ celix_filter_skipWhiteSpace(filterString, pos);
c = filterString[*pos];
switch (c) {
- case '&': {
- (*pos)++;
- return filter_parseAndOrOr(filterString, CELIX_FILTER_OPERAND_AND, pos);
- }
- case '|': {
- (*pos)++;
- return filter_parseAndOrOr(filterString, CELIX_FILTER_OPERAND_OR, pos);
- }
- case '!': {
- (*pos)++;
- return filter_parseNot(filterString, pos);
- }
- default: {
- return filter_parseItem(filterString, pos);
- }
+ case '&': {
+ (*pos)++;
+ return celix_filter_parseAndOrOr(filterString, CELIX_FILTER_OPERAND_AND, pos);
+ }
+ case '|': {
+ (*pos)++;
+ return celix_filter_parseAndOrOr(filterString, CELIX_FILTER_OPERAND_OR, pos);
+ }
+ case '!': {
+ (*pos)++;
+ return celix_filter_parseNot(filterString, pos);
+ }
+ default: {
+ return celix_filter_parseItem(filterString, pos);
+ }
}
}
-static celix_filter_t * filter_parseAndOrOr(char * filterString, celix_filter_operand_t andOrOr, int * pos) {
+static celix_filter_t* celix_filter_parseAndOrOr(char* filterString, celix_filter_operand_t andOrOr, int* pos) {
+ celix_filter_skipWhiteSpace(filterString, pos);
- filter_skipWhiteSpace(filterString, pos);
- bool failure = false;
+ celix_autoptr(celix_filter_t) filter = (celix_filter_t*)calloc(1, sizeof(*filter));
+ if (!filter) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
+ return NULL;
+ }
- if (filterString[*pos] != '(') {
- fprintf(stderr, "Filter Error: Missing '('.\n");
+ celix_autoptr(celix_array_list_t) children = celix_arrayList_create();
+ if (!children) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
return NULL;
}
- celix_array_list_t *children = celix_arrayList_create();
- while(filterString[*pos] == '(') {
- celix_filter_t * child = filter_parseFilter(filterString, pos);
- if(child == NULL) {
- failure = true;
- break;
+ if (filterString[*pos] == ')') {
+ // empty and/or filter
+ } else if (filterString[*pos] != '(') {
+ celix_err_push("Filter Error: Missing '('.");
+ return NULL;
+ } else {
+ bool failure = false;
+ while (filterString[*pos] == '(') {
+ celix_filter_t* child = celix_filter_parseFilter(filterString, pos);
+ if (child == NULL) {
+ failure = true;
+ break;
+ }
+ celix_arrayList_add(children, child);
}
- celix_arrayList_add(children, child);
- }
- if(failure == true){
- for (int i = 0; i < celix_arrayList_size(children); ++i) {
- celix_filter_t * f = celix_arrayList_get(children, i);
- filter_destroy(f);
+ if (failure == true) {
+ for (int i = 0; i < celix_arrayList_size(children); ++i) {
+ celix_filter_t* f = celix_arrayList_get(children, i);
+ filter_destroy(f);
+ }
+ return NULL;
}
- celix_arrayList_destroy(children);
- return NULL;
}
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
filter->operand = andOrOr;
- filter->children = children;
+ filter->children = celix_steal_ptr(children);
- return filter;
+ return celix_steal_ptr(filter);
}
-static celix_filter_t * filter_parseNot(char * filterString, int * pos) {
- celix_filter_t * child = NULL;
- filter_skipWhiteSpace(filterString, pos);
+static celix_filter_t* celix_filter_parseNot(char* filterString, int* pos) {
+ celix_filter_skipWhiteSpace(filterString, pos);
- if (filterString[*pos] != '(') {
- fprintf(stderr, "Filter Error: Missing '('.\n");
+ char c = filterString[*pos];
+ if (c != '(') {
+ celix_err_push("Filter Error: Missing '('.");
return NULL;
}
- child = filter_parseFilter(filterString, pos);
- if (child == NULL) {
+ celix_filter_t* child = celix_filter_parseFilter(filterString, pos);
+ if (!child) {
return NULL;
}
+
celix_array_list_t* children = celix_arrayList_create();
+ if (!children) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
+ celix_filter_destroy(child);
+ return NULL;
+ }
celix_arrayList_add(children, child);
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- filter->operand = CELIX_FILTER_OPERAND_NOT;
- filter->children = children;
+ celix_filter_t* filter = (celix_filter_t*)calloc(1, sizeof(*filter));
+ if (!filter) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
+ celix_filter_destroy(child);
+ celix_arrayList_destroy(children);
+ return NULL;
+ }
+ filter->operand = CELIX_FILTER_OPERAND_NOT;
+ filter->children = children; //note can be NULL
return filter;
}
-static celix_filter_t * filter_parseItem(char * filterString, int * pos) {
- char * attr = filter_parseAttr(filterString, pos);
- if(attr == NULL){
+static celix_filter_t* celix_filter_parseItem(char* filterString, int* pos) {
+ celix_autofree char* attr = celix_filter_parseAttributeOrValue(filterString, pos, true);
+ if (!attr) {
+ celix_err_push("Filter Error: Missing attr.");
return NULL;
}
- filter_skipWhiteSpace(filterString, pos);
- switch(filterString[*pos]) {
- case '~': {
- if (filterString[*pos + 1] == '=') {
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- *pos += 2;
- filter->operand = CELIX_FILTER_OPERAND_APPROX;
- filter->attribute = attr;
- filter->value = filter_parseValue(filterString, pos);
- return filter;
+ celix_autoptr(celix_filter_t) filter = calloc(1, sizeof(*filter));
+ if (!filter) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
+ return NULL;
+ }
+ filter->attribute = celix_steal_ptr(attr);
+
+ celix_filter_skipWhiteSpace(filterString, pos);
+ char op = filterString[*pos];
+ switch (op) {
+ case '~': {
+ char secondOp = filterString[*pos + 1];
+ if (secondOp == '=') {
+ *pos += 2;
+ filter->operand = CELIX_FILTER_OPERAND_APPROX;
+ filter->value = celix_filter_parseAttributeOrValue(filterString, pos, false);
+ if (!filter->value) {
+ return NULL;
}
- break;
+ return celix_steal_ptr(filter);
}
- case '>': {
- if (filterString[*pos + 1] == '=') {
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- *pos += 2;
- filter->operand = CELIX_FILTER_OPERAND_GREATEREQUAL;
- filter->attribute = attr;
- filter->value = filter_parseValue(filterString, pos);
- return filter;
+ celix_err_pushf("Filter Error: Invalid operand char after ~. Expected `=` got `%c`", secondOp);
+ return NULL;
+ }
+ case '>': {
+ if (filterString[*pos + 1] == '=') {
+ *pos += 2;
+ filter->operand = CELIX_FILTER_OPERAND_GREATEREQUAL;
+ filter->value = celix_filter_parseAttributeOrValue(filterString, pos, false);
+ if (!filter->value) {
+ return NULL;
}
- else {
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- *pos += 1;
- filter->operand = CELIX_FILTER_OPERAND_GREATER;
- filter->attribute = attr;
- filter->value = filter_parseValue(filterString, pos);
- return filter;
+ return celix_steal_ptr(filter);
+ } else {
+ *pos += 1;
+ filter->operand = CELIX_FILTER_OPERAND_GREATER;
+ filter->value = celix_filter_parseAttributeOrValue(filterString, pos, false);
+ if (!filter->value) {
+ return NULL;
}
+ return celix_steal_ptr(filter);
}
- case '<': {
- if (filterString[*pos + 1] == '=') {
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- *pos += 2;
- filter->operand = CELIX_FILTER_OPERAND_LESSEQUAL;
- filter->attribute = attr;
- filter->value = filter_parseValue(filterString, pos);
- return filter;
+ }
+ case '<': {
+ if (filterString[*pos + 1] == '=') {
+ *pos += 2;
+ filter->operand = CELIX_FILTER_OPERAND_LESSEQUAL;
+ filter->value = celix_filter_parseAttributeOrValue(filterString, pos, false);
+ if (!filter->value) {
+ return NULL;
}
- else {
- celix_filter_t * filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- *pos += 1;
- filter->operand = CELIX_FILTER_OPERAND_LESS;
- filter->attribute = attr;
- filter->value = filter_parseValue(filterString, pos);
- return filter;
+ return celix_steal_ptr(filter);
+ } else {
+ *pos += 1;
+ filter->operand = CELIX_FILTER_OPERAND_LESS;
+ filter->value = celix_filter_parseAttributeOrValue(filterString, pos, false);
+ if (!filter->value) {
+ return NULL;
}
+ return celix_steal_ptr(filter);
}
- case '=': {
- celix_filter_t * filter = NULL;
- celix_array_list_t *subs;
- if (filterString[*pos + 1] == '*') {
- int oldPos = *pos;
- *pos += 2;
- filter_skipWhiteSpace(filterString, pos);
- if (filterString[*pos] == ')') {
- filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- filter->operand = CELIX_FILTER_OPERAND_PRESENT;
- filter->attribute = attr;
- filter->value = NULL;
- return filter;
- }
- *pos = oldPos;
+ }
+ case '=': {
+ if (filterString[*pos + 1] == '*') {
+ int oldPos = *pos;
+ *pos += 2;
+ celix_filter_skipWhiteSpace(filterString, pos);
+ if (filterString[*pos] == ')') {
+ filter->operand = CELIX_FILTER_OPERAND_PRESENT;
+ filter->value = NULL;
+ return celix_steal_ptr(filter);
}
- filter = (celix_filter_t *) calloc(1, sizeof(*filter));
- (*pos)++;
- subs = filter_parseSubstring(filterString, pos);
- if(subs!=NULL){
- if (celix_arrayList_size(subs) == 1) {
- char * string = (char *) celix_arrayList_get(subs, 0);
- if (string != NULL) {
- filter->operand = CELIX_FILTER_OPERAND_EQUAL;
- filter->attribute = attr;
- filter->value = string;
-
- celix_arrayList_clear(subs);
- celix_arrayList_destroy(subs);
-
- return filter;
- }
- }
+ *pos = oldPos;
+ }
+ (*pos)++;
+ if (celix_filter_isSubString(filterString, *pos)) {
+ celix_array_list_t* subs = celix_filter_parseSubstring(filterString, pos);
+ if (!subs) {
+ return NULL;
}
filter->operand = CELIX_FILTER_OPERAND_SUBSTRING;
- filter->attribute = attr;
filter->children = subs;
- return filter;
- }
- }
- fprintf(stderr, "Filter Error: Invalid operator.\n");
- free(attr);
- return NULL;
-}
-
-static char * filter_parseAttr(char * filterString, int * pos) {
- char c;
- int begin = *pos;
- int end = *pos;
- int length;
-
- filter_skipWhiteSpace(filterString, pos);
- c = filterString[*pos];
-
- while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
- (*pos)++;
-
- if (!isspace(c)) {
- end = *pos;
+ return celix_steal_ptr(filter);
+ } else {
+ filter->operand = CELIX_FILTER_OPERAND_EQUAL;
+ filter->value = celix_filter_parseAttributeOrValue(filterString, pos, false);
+ if (!filter->value) {
+ return NULL;
+ }
+ return celix_steal_ptr(filter);
}
-
- c = filterString[*pos];
}
-
- length = end - begin;
-
- if (length == 0) {
- fprintf(stderr, "Filter Error: Missing attr.\n");
+ default: {
+ celix_err_pushf("Filter Error: Invalid operand char `%c`", op);
return NULL;
- } else {
- char * attr = (char *) calloc(1, length+1);
- strncpy(attr, filterString+begin, length);
- attr[length] = '\0';
- return attr;
+ }
}
}
-static char * filter_parseValue(char * filterString, int * pos) {
- char *value = calloc(strlen(filterString) + 1, sizeof(*value));
- int keepRunning = 1;
+static char* celix_filter_parseAttributeOrValue(const char* filterString, int* pos, bool parseAttribute) {
+ const char* name = parseAttribute ? "attribute" : "attribute value";
+ celix_autofree char* value = NULL;
+ size_t valueSize = 0;
+ celix_autoptr(FILE) stream = open_memstream(&value, &valueSize);
+ if (!stream) {
+ celix_err_push("Filter Error: Failed to open mem stream.");
+ return NULL;
+ }
+ bool keepRunning = true;
while (keepRunning) {
char c = filterString[*pos];
switch (c) {
- case ')': {
- keepRunning = 0;
- break;
- }
- case '(': {
- fprintf(stderr, "Filter Error: Invalid value.\n");
- free(value);
+ case ')': {
+ if (parseAttribute) {
+ celix_err_pushf("Filter Error: Unexpected `)` char while parsing %s.", name);
+ fflush(stream);
return NULL;
}
- case '\0':{
- fprintf(stderr, "Filter Error: Unclosed bracket.\n");
- free(value);
- return NULL;
+ keepRunning = false;
+ break;
+ }
+ case '(': {
+ celix_err_pushf("Filter Error: Unexpected `(` char while parsing %s.", name);
+ fflush(stream);
+ return NULL;
+ }
+ case '\0': {
+ celix_err_pushf("Filter Error: Unexpected end of string while parsing %s.", name);
+ fflush(stream);
+ return NULL;
+ }
+ case '<':
+ case '>':
+ case '=':
+ case '~': {
+ if (parseAttribute) {
+ keepRunning = false; // done for attribute, valid for value
+ break;
}
- case '\\': {
- (*pos)++;
+ /* no break */
+ }
+ default: {
+ if (c == '\\') {
+ if (filterString[*pos + 1] == '\0') {
+ celix_err_pushf("Filter Error: Unexpected end of string while parsing %s.", name);
+ fflush(stream);
+ return NULL;
+ }
+ (*pos)++; // eat '\'
c = filterString[*pos];
}
- /* no break */
- default: {
- char ch[2];
- ch[0] = c;
- ch[1] = '\0';
- strcat(value, ch);
- (*pos)++;
- break;
+ int rc = fputc(c, stream);
+ if (rc == EOF) {
+ celix_err_push("Filter Error: Failed to write to stream.");
+ fflush(stream);
+ return NULL;
}
+ (*pos)++;
+ break;
}
+ }
+ }
+
+ // end with \0
+ int rc = fputc('\0', stream);
+ if (rc == EOF) {
+ celix_err_push("Filter Error: Failed to write to stream.");
+ fflush(stream);
+ return NULL;
+ }
+
+ rc = fclose(celix_steal_ptr(stream));
+ if (rc != 0) {
+ celix_err_push("Filter Error: Failed to close stream.");
+ return NULL;
}
- if (strlen(value) == 0) {
- fprintf(stderr, "Filter Error: Missing value.\n");
- free(value);
+ if (celix_utils_strlen(value) == 0) {
+ celix_err_push("Filter Error: Empty value.\n");
return NULL;
}
- return value;
+
+ return celix_steal_ptr(value);
}
-static celix_array_list_t* filter_parseSubstring(char * filterString, int * pos) {
- char *sub = calloc(strlen(filterString) + 1, sizeof(*sub));
- celix_array_list_t* operands = celix_arrayList_create();
- int keepRunning = 1;
+static bool celix_filter_isSubString(const char* filterString, int startPos) {
+ for (int i = startPos; filterString[i] != '\0' && filterString[i] != ')'; i++) {
+ if (filterString[i] == '*') {
+ return true;
+ }
+ }
+ return false;
+}
- while (keepRunning) {
- char c = filterString[*pos];
-
+celix_status_t celix_filter_parseSubstringAny(char* filterString, int* pos, char** out) {
+ celix_autofree char* any = NULL;
+ size_t anySize = 0;
+ celix_autoptr(FILE) stream = open_memstream(&any, &anySize);
+ if (!stream) {
+ celix_err_push("Filter Error: Failed to open mem stream.");
+ return CELIX_ENOMEM;
+ }
- switch (c) {
- case ')': {
- if (strlen(sub) > 0) {
- celix_arrayList_add(operands, strdup(sub));
- }
- keepRunning = 0;
- break;
- }
- case '\0':{
- fprintf(stderr, "Filter Error: Unclosed bracket.\n");
- keepRunning = false;
- break;
- }
- case '(': {
- fprintf(stderr, "Filter Error: Invalid value.\n");
- keepRunning = false;
- break;
- }
- case '*': {
- if (strlen(sub) > 0) {
- celix_arrayList_add(operands, strdup(sub));
- }
- sub[0] = '\0';
- celix_arrayList_add(operands, NULL);
- (*pos)++;
- break;
- }
- case '\\': {
- (*pos)++;
- c = filterString[*pos];
- }
- /* no break */
- default: {
- char ch[2];
- ch[0] = c;
- ch[1] = '\0';
- strcat(sub, ch);
- (*pos)++;
- break;
+ int startPos = *pos;
+ while (filterString[*pos] != '\0' && filterString[*pos] != ')' && filterString[*pos] != '*') {
+ int rc;
+ if (filterString[*pos] == '\\') {
+ if (filterString[*pos + 1] == '\0') {
+ celix_err_push("Filter Error: Unexpected end of string while parsing attribute value.");
+ fflush(stream);
+ return CELIX_FILE_IO_EXCEPTION;
}
+ (*pos)++; // eat '\'
+ rc = fputc(filterString[*pos], stream);
+ } else {
+ rc = fputc(filterString[*pos], stream);
}
+ if (rc == EOF) {
+ celix_err_push("Filter Error: Failed to write to stream.\n");
+ fflush(stream);
+ return CELIX_FILE_IO_EXCEPTION;
+ }
+ (*pos)++;
}
- free(sub);
- if (celix_arrayList_size(operands) == 0) {
- fprintf(stderr, "Filter Error: Missing value.\n");
- celix_arrayList_destroy(operands);
+ if (startPos == *pos) {
+ // empty any
+ *out = NULL;
+ return CELIX_SUCCESS;
+ }
+
+ int rc = fclose(celix_steal_ptr(stream));
+ if (rc != 0) {
+ celix_err_push("Filter Error: Failed to close stream.");
+ return CELIX_FILE_IO_EXCEPTION;
+ }
+
+ *out = celix_steal_ptr(any);
+ return CELIX_SUCCESS;
+}
+
+/**
+ * @brief Parses a substring filter.
+ * Returns a array list with at least 2 elements: The first element is the initial element, the last element is the
+ * final element. The initial and final element can be NULL.
+ *
+ * e.g.:
+ * - (foo=bar*) -> [bar, NULL]
+ * - (foo=*bar) -> [NULL, bar]
+ * - (foo=*bar*) -> [NULL, bar, NULL]
+ * - (foo=bar*bar) -> [bar, bar]
+ * - (foo=bar*bar*) -> [bar, bar, NULL]
+ */
+static celix_array_list_t* celix_filter_parseSubstring(char* filterString, int* pos) {
+ celix_array_list_t* subs = celix_arrayList_create();
+ if (!subs) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
return NULL;
}
- return operands;
+ celix_filter_skipWhiteSpace(filterString, pos);
+ if (filterString[*pos] == '*') {
+ // initial substring is NULL
+ // eat '*'
+ (*pos)++;
+ celix_arrayList_add(subs, NULL);
+ }
+
+ char* element = NULL;
+ celix_status_t status;
+ do {
+ status = celix_filter_parseSubstringAny(filterString, pos, &element);
+ if (element) {
+ celix_arrayList_add(subs, element);
+ }
+
+ if (filterString[*pos] == '*' && celix_filter_isNextNonWhiteSpaceChar(filterString, *pos + 1, ')')) {
+ // final substring is NULL
+ (*pos)++; // eat '*'
+ celix_filter_skipWhiteSpace(filterString, pos);
+ celix_arrayList_add(subs, NULL);
+ break;
+ } else if (filterString[*pos] == '*') {
+ // eat '*'
+ (*pos)++;
+ }
+ } while (status == CELIX_SUCCESS && element);
+
+ if (status != CELIX_SUCCESS) {
+ for (int i = 0; i < celix_arrayList_size(subs); ++i) {
+ char* e = celix_arrayList_get(subs, i);
+ free(e);
+ }
+ celix_arrayList_destroy(subs);
+ return NULL;
+ }
+
+ celix_filter_skipWhiteSpace(filterString, pos);
+ return subs;
}
static bool celix_filter_isCompareOperand(celix_filter_operand_t operand) {
- return operand == CELIX_FILTER_OPERAND_EQUAL ||
- operand == CELIX_FILTER_OPERAND_GREATER ||
- operand == CELIX_FILTER_OPERAND_LESS ||
- operand == CELIX_FILTER_OPERAND_GREATEREQUAL ||
- operand == CELIX_FILTER_OPERAND_LESSEQUAL;
+ return operand == CELIX_FILTER_OPERAND_EQUAL || operand == CELIX_FILTER_OPERAND_GREATER ||
+ operand == CELIX_FILTER_OPERAND_LESS || operand == CELIX_FILTER_OPERAND_GREATEREQUAL ||
+ operand == CELIX_FILTER_OPERAND_LESSEQUAL;
}
-
static bool celix_filter_hasFilterChildren(celix_filter_t* filter) {
- return filter->operand == CELIX_FILTER_OPERAND_AND ||
- filter->operand == CELIX_FILTER_OPERAND_OR ||
+ return filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR ||
filter->operand == CELIX_FILTER_OPERAND_NOT;
}
@@ -444,14 +535,19 @@ static bool celix_filter_hasFilterChildren(celix_filter_t* filter) {
*/
static celix_status_t celix_filter_compile(celix_filter_t* filter) {
if (celix_filter_isCompareOperand(filter->operand)) {
- filter->internal = malloc(sizeof(*filter->internal));
+ filter->internal = calloc(1, sizeof(*filter->internal));
if (filter->internal == NULL) {
+ celix_err_push("Filter Error: Failed to allocate memory.");
return CELIX_ENOMEM;
} else {
- filter->internal->longValue = celix_utils_convertStringToLong(filter->value, 0, &filter->internal->convertedToLong);
- filter->internal->doubleValue = celix_utils_convertStringToDouble(filter->value, 0.0, &filter->internal->convertedToDouble);
- filter->internal->versionValue = celix_utils_convertStringToVersion(filter->value, NULL, &filter->internal->convertedToVersion);
- filter->internal->boolValue = celix_utils_convertStringToBool(filter->value, false, &filter->internal->convertedToBool);
+ filter->internal->longValue =
+ celix_utils_convertStringToLong(filter->value, 0, &filter->internal->convertedToLong);
+ filter->internal->doubleValue =
+ celix_utils_convertStringToDouble(filter->value, 0.0, &filter->internal->convertedToDouble);
+ filter->internal->versionValue =
+ celix_utils_convertStringToVersion(filter->value, NULL, &filter->internal->convertedToVersion);
+ filter->internal->boolValue =
+ celix_utils_convertStringToBool(filter->value, false, &filter->internal->convertedToBool);
}
}
@@ -513,7 +609,7 @@ static int celix_filter_compareAttributeValue(const celix_filter_t* filter, cons
return strcmp(entry->value, filter->value);
}
- //compare typed values
+ // compare typed values
if (filter->internal->convertedToLong && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
return celix_filter_cmpLong(entry->typed.longValue, filter->internal->longValue);
} else if (filter->internal->convertedToDouble && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
@@ -524,7 +620,7 @@ static int celix_filter_compareAttributeValue(const celix_filter_t* filter, cons
return celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue);
}
- //check if the property string value can be converted to the filter value type
+ // check if the property string value can be converted to the filter value type
bool propertyConverted;
if (filter->internal->convertedToLong) {
long val = celix_utils_convertStringToLong(entry->value, 0, &propertyConverted);
@@ -550,100 +646,74 @@ static int celix_filter_compareAttributeValue(const celix_filter_t* filter, cons
}
}
- //fallback on string compare
+ // fallback on string compare
return strcmp(entry->value, filter->value);
}
-static bool filter_compareSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
- const char* propertyValue = entry->value;
- size_t pos = 0;
- int size = celix_arrayList_size(filter->children);
- for (size_t i = 0; i < size; i++) {
- char * substr = (char *) celix_arrayList_get(filter->children, (int)i);
+static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+ assert(filter->children && celix_arrayList_size(filter->children) >= 2);
- if (i + 1 < size) {
- if (substr == NULL) {
- unsigned int index;
- char * substr2 = (char *) celix_arrayList_get(filter->children, (int)(i + 1));
- if (substr2 == NULL) {
- continue;
- }
- index = strcspn(propertyValue+pos, substr2);
- if (index == strlen(propertyValue+pos)) {
- return false;
- }
+ size_t strLen = celix_utils_strlen(entry->value);
+ const char* initial = celix_arrayList_get(filter->children, 0);
+ const char* final = celix_arrayList_get(filter->children, celix_arrayList_size(filter->children) - 1);
- pos = index + strlen(substr2);
- if (i + 2 < size) {
- i++;
- }
- } else {
- unsigned int len = strlen(substr);
- 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);
- return false;
- }
- free(region);
- }
- } else {
- size_t len;
- size_t begin;
+ const char* currentValue = entry->value;
- if (substr == NULL) {
- return true;
- }
- len = strlen(substr);
- begin = strlen(propertyValue)-len;
- return (strcmp(propertyValue+begin, substr) == 0);
+ if (initial) {
+ const char* found = strstr(entry->value, initial);
+ currentValue = found + celix_utils_strlen(initial);
+ if (!found || found != entry->value) {
+ return false;
+ }
+ }
+
+ for (int i = 1; i < celix_arrayList_size(filter->children) - 1; i++) {
+ const char* substr = celix_arrayList_get(filter->children, i);
+ const char* found = strstr(currentValue, substr);
+ if (!found || found <= currentValue) {
+ return false;
}
+ currentValue = found + celix_utils_strlen(substr);
}
+
+ if (final) {
+ const char* found = strstr(currentValue, final);
+ if (!found || found <= currentValue || found + celix_utils_strlen(final) != entry->value + strLen) {
+ return false;
+ }
+ }
+
return true;
}
static bool celix_filter_matchPropertyEntry(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
- if (filter == NULL || entry == NULL) {
- return false;
+ switch (filter->operand) {
+ case CELIX_FILTER_OPERAND_SUBSTRING:
+ return celix_filter_matchSubString(filter, entry);
+ case CELIX_FILTER_OPERAND_APPROX:
+ return strcasecmp(entry->value, filter->value) == 0;
+ case CELIX_FILTER_OPERAND_EQUAL:
+ return celix_filter_compareAttributeValue(filter, entry) == 0;
+ case CELIX_FILTER_OPERAND_GREATER:
+ return celix_filter_compareAttributeValue(filter, entry) > 0;
+ case CELIX_FILTER_OPERAND_GREATEREQUAL:
+ return celix_filter_compareAttributeValue(filter, entry) >= 0;
+ case CELIX_FILTER_OPERAND_LESS:
+ return celix_filter_compareAttributeValue(filter, entry) < 0;
+ default:
+ assert(filter->operand == CELIX_FILTER_OPERAND_LESSEQUAL);
+ return celix_filter_compareAttributeValue(filter, entry) <= 0;
}
+}
- switch (filter->operand) {
- case CELIX_FILTER_OPERAND_SUBSTRING:
- return filter_compareSubString(filter, entry);
- case CELIX_FILTER_OPERAND_APPROX:
- return strcasecmp(entry->value, filter->value) == 0;
- case CELIX_FILTER_OPERAND_EQUAL:
- return celix_filter_compareAttributeValue(filter, entry) == 0;
- case CELIX_FILTER_OPERAND_GREATER:
- return celix_filter_compareAttributeValue(filter, entry) > 0;
- case CELIX_FILTER_OPERAND_GREATEREQUAL:
- return celix_filter_compareAttributeValue(filter, entry) >= 0;
- case CELIX_FILTER_OPERAND_LESS:
- return celix_filter_compareAttributeValue(filter, entry) < 0;
- case CELIX_FILTER_OPERAND_LESSEQUAL:
- 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:
- //LCOV EXCL START
- return false; //should not happen
- //LCOV EXCL STOP
- }
-}
-
-celix_status_t filter_getString(celix_filter_t * filter, const char **filterStr) {
+celix_status_t filter_getString(celix_filter_t* filter, const char** filterStr) {
if (filter != NULL) {
*filterStr = filter->filterStr;
}
return CELIX_SUCCESS;
}
-celix_status_t filter_match_filter(celix_filter_t *src, celix_filter_t *dest, bool *out) {
+celix_status_t filter_match_filter(celix_filter_t* src, celix_filter_t* dest, bool* out) {
bool result = celix_filter_equals(src, dest);
if (out != NULL) {
*out = result;
@@ -651,8 +721,11 @@ celix_status_t filter_match_filter(celix_filter_t *src, celix_filter_t *dest, bo
return CELIX_SUCCESS;
}
+celix_filter_t* celix_filter_create(const char* filterString) {
+ if (!filterString || strnlen(filterString, 1) == 0) {
+ filterString = "(|)";
+ }
-celix_filter_t* celix_filter_create(const char *filterString) {
celix_autofree char* str = celix_utils_strdup(filterString);
if (!str) {
celix_err_push("Failed to create filter string");
@@ -660,13 +733,13 @@ celix_filter_t* celix_filter_create(const char *filterString) {
}
int pos = 0;
- celix_autoptr(celix_filter_t) filter = filter_parseFilter(str, &pos);
+ celix_autoptr(celix_filter_t) filter = celix_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");
+ celix_err_push("Filter Error: Extraneous trailing characters.");
return NULL;
}
@@ -674,14 +747,14 @@ celix_filter_t* celix_filter_create(const char *filterString) {
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");
+ celix_err_push("Filter Error: Missing attribute or value.");
return NULL;
}
}
filter->filterStr = celix_steal_ptr(str);
if (celix_filter_compile(filter) != CELIX_SUCCESS) {
- // TODO test error
+ celix_err_push("Failed to compile filter");
return NULL;
}
return celix_steal_ptr(filter);
@@ -725,24 +798,27 @@ void celix_filter_destroy(celix_filter_t* filter) {
}
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
+ if (!filter || !properties) {
+ return false; // matching on null filter or property is always false
}
- switch (filter->operand) {
- 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);
+ if (filter->operand == CELIX_FILTER_OPERAND_PRESENT) {
+ return celix_properties_get(properties, filter->attribute, NULL) != NULL;
+ } else if (filter->operand == CELIX_FILTER_OPERAND_AND) {
+ 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 false;
}
}
return true;
- }
- case CELIX_FILTER_OPERAND_OR: {
+ } else if (filter->operand == CELIX_FILTER_OPERAND_OR) {
celix_array_list_t* children = filter->children;
+ if (celix_arrayList_size(children) == 0) {
+ return true;
+ }
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);
@@ -751,35 +827,21 @@ bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t*
}
}
return false;
- }
- case CELIX_FILTER_OPERAND_NOT: {
+ } else if (filter->operand == 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
- return false;// should not happen
- // LCOV_EXCL_STOP
+ // substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry
+ const celix_properties_entry_t* entry = celix_properties_getEntry(properties, filter->attribute);
+ if (!entry) {
+ return false;
+ }
+ return celix_filter_matchPropertyEntry(filter, entry);
}
-bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *filter2) {
+bool celix_filter_equals(const celix_filter_t* filter1, const celix_filter_t* filter2) {
if (filter1 == filter2) {
return true;
}
@@ -790,8 +852,8 @@ bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *fi
return false;
}
-
- if (filter1->operand == CELIX_FILTER_OPERAND_AND || filter1->operand == CELIX_FILTER_OPERAND_OR || filter1->operand == CELIX_FILTER_OPERAND_NOT) {
+ 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);
@@ -800,11 +862,11 @@ bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *fi
int i;
int k;
int sameCount = 0;
- for (i =0; i < sizeSrc; ++i) {
+ for (i = 0; i < sizeSrc; ++i) {
bool same = false;
- celix_filter_t *srcPart = celix_arrayList_get(filter1->children, i);
+ 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);
+ celix_filter_t* destPart = celix_arrayList_get(filter2->children, k);
filter_match_filter(srcPart, destPart, &same);
if (same) {
sameCount += 1;
@@ -817,7 +879,7 @@ bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *fi
return false;
}
- //compare attr and value
+ // compare attr and value
bool attrSame = false;
bool valSame = false;
if (filter1->attribute == NULL && filter2->attribute == NULL) {
@@ -826,7 +888,7 @@ bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *fi
attrSame = celix_utils_stringEquals(filter1->attribute, filter2->attribute);
}
- if (filter1->value == NULL && filter2->value == NULL) {
+ 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);
@@ -835,20 +897,21 @@ bool celix_filter_equals(const celix_filter_t *filter1, const celix_filter_t *fi
return attrSame && valSame;
}
-const char* celix_filter_getFilterString(const celix_filter_t *filter) {
+const char* celix_filter_getFilterString(const celix_filter_t* filter) {
if (filter != NULL) {
return filter->filterStr;
}
return NULL;
}
-const char* celix_filter_findAttribute(const celix_filter_t *filter, const char *attribute) {
- const char *result = NULL;
+const char* celix_filter_findAttribute(const celix_filter_t* filter, const char* attribute) {
+ const char* result = NULL;
if (filter != NULL && attribute != NULL) {
- if (filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR || filter->operand == CELIX_FILTER_OPERAND_NOT) {
+ if (filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR ||
+ filter->operand == CELIX_FILTER_OPERAND_NOT) {
int size = celix_arrayList_size(filter->children);
for (int i = 0; i < size; ++i) {
- celix_filter_t *child = celix_arrayList_get(filter->children, i);
+ celix_filter_t* child = celix_arrayList_get(filter->children, i);
result = celix_filter_findAttribute(child, attribute);
if (result != NULL) {
break;
@@ -861,21 +924,22 @@ const char* celix_filter_findAttribute(const celix_filter_t *filter, const char
return result;
}
-static bool hasMandatoryEqualsValueAttribute(const celix_filter_t *filter, const char *attribute, bool negated, bool optional) {
+static bool
+hasMandatoryEqualsValueAttribute(const celix_filter_t* filter, const char* attribute, bool negated, bool optional) {
bool equalsValueAttribute = false;
if (filter != NULL && attribute != NULL) {
- if (filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR || filter->operand == CELIX_FILTER_OPERAND_NOT) {
+ if (filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR ||
+ filter->operand == CELIX_FILTER_OPERAND_NOT) {
int size = celix_arrayList_size(filter->children);
for (int i = 0; i < size; ++i) {
-
if (filter->operand == CELIX_FILTER_OPERAND_NOT) {
negated = !negated;
} else if (filter->operand == CELIX_FILTER_OPERAND_OR) {
optional = true;
}
- celix_filter_t *child = celix_arrayList_get(filter->children, i);
+ celix_filter_t* child = celix_arrayList_get(filter->children, i);
equalsValueAttribute = hasMandatoryEqualsValueAttribute(child, attribute, negated, optional);
@@ -891,25 +955,26 @@ static bool hasMandatoryEqualsValueAttribute(const celix_filter_t *filter, const
return equalsValueAttribute;
}
-bool celix_filter_hasMandatoryEqualsValueAttribute(const celix_filter_t *filter, const char *attribute) {
+bool celix_filter_hasMandatoryEqualsValueAttribute(const celix_filter_t* filter, const char* attribute) {
return hasMandatoryEqualsValueAttribute(filter, attribute, false, false);
}
-static bool hasMandatoryNegatedPresenceAttribute(const celix_filter_t *filter, const char *attribute, bool negated, bool optional) {
+static bool
+hasMandatoryNegatedPresenceAttribute(const celix_filter_t* filter, const char* attribute, bool negated, bool optional) {
bool negatedPresenceAttribute = false;
if (filter != NULL && attribute != NULL) {
- if (filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR || filter->operand == CELIX_FILTER_OPERAND_NOT) {
+ if (filter->operand == CELIX_FILTER_OPERAND_AND || filter->operand == CELIX_FILTER_OPERAND_OR ||
+ filter->operand == CELIX_FILTER_OPERAND_NOT) {
int size = celix_arrayList_size(filter->children);
for (int i = 0; i < size; ++i) {
-
if (filter->operand == CELIX_FILTER_OPERAND_NOT) {
negated = !negated;
} else if (filter->operand == CELIX_FILTER_OPERAND_OR) {
optional = true;
}
- celix_filter_t *child = celix_arrayList_get(filter->children, i);
+ celix_filter_t* child = celix_arrayList_get(filter->children, i);
negatedPresenceAttribute = hasMandatoryNegatedPresenceAttribute(child, attribute, negated, optional);
@@ -924,8 +989,8 @@ static bool hasMandatoryNegatedPresenceAttribute(const celix_filter_t *filter, c
return negatedPresenceAttribute;
}
-bool celix_filter_hasMandatoryNegatedPresenceAttribute(const celix_filter_t *filter, const char *attribute) {
+bool celix_filter_hasMandatoryNegatedPresenceAttribute(const celix_filter_t* filter, const char* attribute) {
return hasMandatoryNegatedPresenceAttribute(filter, attribute, false, false);
}
-//NOLINTEND(misc-no-recursion)
+// NOLINTEND(misc-no-recursion)