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 2024/01/07 19:43:19 UTC
(celix) 08/08: Add support for filter match with an array property set entry
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/674-improve-properties
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 968cc8ae7ddaad1f570e78deba5755d6f0d008ae
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Jan 7 20:41:50 2024 +0100
Add support for filter match with an array property set entry
---
libs/utils/gtest/src/CxxFilterTestSuite.cc | 82 ++++-----
libs/utils/gtest/src/FilterTestSuite.cc | 113 +++++++++++++
libs/utils/include/celix_filter.h | 44 ++++-
libs/utils/src/filter.c | 263 ++++++++++++++++++-----------
4 files changed, 359 insertions(+), 143 deletions(-)
diff --git a/libs/utils/gtest/src/CxxFilterTestSuite.cc b/libs/utils/gtest/src/CxxFilterTestSuite.cc
index 549eabba..8369883a 100644
--- a/libs/utils/gtest/src/CxxFilterTestSuite.cc
+++ b/libs/utils/gtest/src/CxxFilterTestSuite.cc
@@ -113,55 +113,47 @@ TEST_F(CxxFilterTestSuite, HasMandatoryNegatedPresenceAttribute) {
}
TEST_F(CxxFilterTestSuite, FilterForLongAttribute) {
- celix::Filter filter1{"(key1>=10)"};
- EXPECT_TRUE(filter1.match({{"key1", "12"}}));
- EXPECT_TRUE(filter1.match({{"key1", "11.1"}}));
- EXPECT_TRUE(filter1.match({{"key1", "10"}}));
- EXPECT_FALSE(filter1.match({{"key1", "2"}}));
- EXPECT_FALSE(filter1.match({{"key1", "1"}}));
- EXPECT_FALSE(filter1.match({{"key1", "0.1"}}));
-
- celix::Filter filter2{"(key1>20)"};
- EXPECT_TRUE(filter2.match({{"key1", "21"}}));
- EXPECT_TRUE(filter2.match({{"key1", "20.1"}}));
- EXPECT_FALSE(filter2.match({{"key1", "20"}}));
- EXPECT_FALSE(filter2.match({{"key1", "3"}}));
- EXPECT_FALSE(filter2.match({{"key1", "2"}}));
- EXPECT_FALSE(filter2.match({{"key1", "0.1"}}));
+ celix::Properties props{};
+ props.set("key1", 1L);
+ props.set("key2", 20L);
+
+ EXPECT_EQ(celix::Properties::ValueType::Long, props.getType("key1"));
+ EXPECT_EQ(celix::Properties::ValueType::Long, props.getType("key2"));
+
+ celix::Filter filter1{"(key1>=1)"};
+ EXPECT_TRUE(filter1.match(props));
+ celix::Filter filter2{"(key2<=3)"};
+ EXPECT_FALSE(filter2.match(props));
+ celix::Filter filter3{"(key2>=1)"};
+ EXPECT_TRUE(filter3.match(props));
}
TEST_F(CxxFilterTestSuite, FilterForDoubleAttribute) {
- celix::Filter filter1{"(key1>=10.5)"};
- EXPECT_TRUE(filter1.match({{"key1", "12"}}));
- EXPECT_TRUE(filter1.match({{"key1", "11.1"}}));
- EXPECT_TRUE(filter1.match({{"key1", "10.5"}}));
- EXPECT_FALSE(filter1.match({{"key1", "2"}}));
- EXPECT_FALSE(filter1.match({{"key1", "1"}}));
- EXPECT_FALSE(filter1.match({{"key1", "0.1"}}));
-
- celix::Filter filter2{"(key1>20.5)"};
- EXPECT_TRUE(filter2.match({{"key1", "21"}}));
- EXPECT_TRUE(filter2.match({{"key1", "20.7"}}));
- EXPECT_FALSE(filter2.match({{"key1", "20.5"}}));
- EXPECT_FALSE(filter2.match({{"key1", "3"}}));
- EXPECT_FALSE(filter2.match({{"key1", "2"}}));
- EXPECT_FALSE(filter2.match({{"key1", "0.1"}}));
+ celix::Properties props{};
+ props.set("key1", 1.1);
+ props.set("key2", 20.2);
+
+ EXPECT_EQ(celix::Properties::ValueType::Double, props.getType("key1"));
+ EXPECT_EQ(celix::Properties::ValueType::Double, props.getType("key2"));
+
+ celix::Filter filter1{"(key1>=1.1)"};
+ EXPECT_TRUE(filter1.match(props));
+ celix::Filter filter2{"(key2<=3.3)"};
+ EXPECT_FALSE(filter2.match(props));
}
TEST_F(CxxFilterTestSuite, FilterForVersionAttribute) {
- celix::Filter filter1{"(key1>=1.2.3.qualifier)"};
- EXPECT_TRUE(filter1.match({{"key1", "1.2.3.qualifier"}}));
- EXPECT_TRUE(filter1.match({{"key1", "2"}}));
- EXPECT_TRUE(filter1.match({{"key1", "2.0.0"}}));
- EXPECT_TRUE(filter1.match({{"key1", "1.2.4"}}));
- EXPECT_FALSE(filter1.match({{"key1", "1.2.2"}}));
- EXPECT_FALSE(filter1.match({{"key1", "1.0.0"}}));
- EXPECT_FALSE(filter1.match({{"key1", "0.1"}}));
-
- celix::Filter filter2{"(key1>2.3.4)"};
- EXPECT_TRUE(filter2.match({{"key1", "3"}}));
- EXPECT_TRUE(filter2.match({{"key1", "3.0.0"}}));
- EXPECT_TRUE(filter2.match({{"key1", "2.3.5"}}));
- EXPECT_FALSE(filter2.match({{"key1", "2.3.4"}}));
- EXPECT_FALSE(filter2.match({{"key1", "0.0.3"}}));
+ celix::Properties props{};
+ props.set("key1", celix::Version{1, 2, 3});
+ props.set("key2", celix::Version{2, 0, 0});
+
+ EXPECT_EQ(celix::Properties::ValueType::Version, props.getType("key1"));
+ EXPECT_EQ(celix::Properties::ValueType::Version, props.getType("key2"));
+
+ celix::Filter filter1{"(key1>=1.2.3)"};
+ EXPECT_TRUE(filter1.match(props));
+ celix::Filter filter2{"(key2<=2.0.0)"};
+ EXPECT_TRUE(filter2.match(props));
+ celix::Filter filter3{"(key2<=1.2.3)"};
+ EXPECT_FALSE(filter3.match(props));
}
diff --git a/libs/utils/gtest/src/FilterTestSuite.cc b/libs/utils/gtest/src/FilterTestSuite.cc
index 22903cd1..8b770830 100644
--- a/libs/utils/gtest/src/FilterTestSuite.cc
+++ b/libs/utils/gtest/src/FilterTestSuite.cc
@@ -589,6 +589,119 @@ TEST_F(FilterTestSuite, InvalidEscapeTest) {
EXPECT_EQ(nullptr, celix_filter_create("(\\")); //escape without following char
}
+TEST_F(FilterTestSuite, UnmatchedTypeMatchTest) {
+ celix_autoptr(celix_properties_t) props = celix_properties_create();
+ celix_properties_set(props, "str", "20");
+ celix_properties_setLong(props, "long", 20);
+
+ celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(str<3)");
+ EXPECT_TRUE(filter1 != nullptr); //note string is compared as string
+
+ celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(long<3)");
+ EXPECT_TRUE(filter2 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter2, props)); //note long is compared as long
+}
+
+TEST_F(FilterTestSuite, MatchArrayTypesTest) {
+ const char* strings[] = {"a", "b", "c"};
+ const long longs[] = {1, 2, 3};
+ const double doubles[] = {1.0, 2.0, 3.0};
+ const bool bools[] = {true, true, true};
+ celix_autoptr(celix_version_t) v1 = celix_version_createVersionFromString("1.0.0");
+ celix_autoptr(celix_version_t) v2 = celix_version_createVersionFromString("2.0.0");
+ celix_autoptr(celix_version_t) v3 = celix_version_createVersionFromString("3.0.0");
+ const celix_version_t* versions[] = {v1, v2, v3};
+
+ celix_autoptr(celix_properties_t) props = celix_properties_create();
+ celix_properties_setStrings(props, "strings", strings, 3);
+ celix_properties_setLongs(props, "longs", longs, 3);
+ celix_properties_setDoubles(props, "doubles", doubles, 3);
+ celix_properties_setBooleans(props, "bools", bools, 3);
+ celix_properties_setVersions(props, "versions", versions, 3);
+
+ // Check if match is true if any of the array elements match
+ celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=a)");
+ EXPECT_TRUE(filter1 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter1, props));
+
+ celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(longs<2)");
+ EXPECT_TRUE(filter2 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter2, props));
+
+ celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(doubles>2.9)");
+ EXPECT_TRUE(filter3 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter3, props));
+
+ celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(bools=true)");
+ EXPECT_TRUE(filter4 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter4, props));
+
+ celix_autoptr(celix_filter_t) filter5 = celix_filter_create("(&(versions>=2.0.0)(versions<=2.0.0))");
+ EXPECT_TRUE(filter5 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter5, props));
+
+ // Check if match is false if none of the array elements match
+ celix_autoptr(celix_filter_t) filter6 = celix_filter_create("(strings=x)");
+ EXPECT_TRUE(filter6 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter6, props));
+
+ celix_autoptr(celix_filter_t) filter7 = celix_filter_create("(longs>3)");
+ EXPECT_TRUE(filter7 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter7, props));
+
+ celix_autoptr(celix_filter_t) filter8 = celix_filter_create("(doubles<0.9)");
+ EXPECT_TRUE(filter8 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter8, props));
+
+ celix_autoptr(celix_filter_t) filter9 = celix_filter_create("(bools=false)");
+ EXPECT_TRUE(filter9 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter9, props));
+
+ celix_autoptr(celix_filter_t) filter10 = celix_filter_create("(&(versions>=4.0.0)(versions<=4.0.0))");
+ EXPECT_TRUE(filter10 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter10, props));
+}
+
+TEST_F(FilterTestSuite, ApproxWithArrayAttributesTest) {
+ const char* strings[] = {"abcdef", "defghi", "ghijkl"};
+ celix_autoptr(celix_properties_t) props = celix_properties_create();
+ celix_properties_setStrings(props, "strings", strings, 3);
+
+ celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings~=abc)");
+ EXPECT_TRUE(filter1 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter1, props));
+
+ celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(strings~=def)");
+ EXPECT_TRUE(filter2 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter2, props));
+
+ celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings~=jkl)");
+ EXPECT_TRUE(filter3 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter3, props));
+
+ celix_autoptr(celix_filter_t) filter4 = celix_filter_create("(strings~=mno)");
+ EXPECT_TRUE(filter4 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter4, props));
+}
+
+TEST_F(FilterTestSuite, SubStringWithArrayAttributesTest) {
+ const char* strings[] = {"John Doe", "Jane Doe", "John Smith"};
+ celix_autoptr(celix_properties_t) props = celix_properties_create();
+ celix_properties_setStrings(props, "strings", strings, 3);
+
+ celix_autoptr(celix_filter_t) filter1 = celix_filter_create("(strings=John*)");
+ EXPECT_TRUE(filter1 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter1, props));
+
+ celix_autoptr(celix_filter_t) filter2 = celix_filter_create("(strings=*Doe)");
+ EXPECT_TRUE(filter2 != nullptr);
+ EXPECT_TRUE(celix_filter_match(filter2, props));
+
+ // check if match is false if none of the array elements match with a substring
+ celix_autoptr(celix_filter_t) filter3 = celix_filter_create("(strings=*Johnson)");
+ EXPECT_TRUE(filter3 != nullptr);
+ EXPECT_FALSE(celix_filter_match(filter3, props));
+}
#include "filter.h"
TEST_F(FilterTestSuite, DeprecatedApiTest) {
diff --git a/libs/utils/include/celix_filter.h b/libs/utils/include/celix_filter.h
index bb2ed4ea..fcb4c3d0 100644
--- a/libs/utils/include/celix_filter.h
+++ b/libs/utils/include/celix_filter.h
@@ -21,6 +21,7 @@
* @file celix_filter.h
* @brief Header file for the Celix Filter API.
*
+ * #Introduction
* An Apache Celix filter is a based on LDAP filters, for more information about LDAP filters see:
* - https://tools.ietf.org/html/rfc4515
* - https://tools.ietf.org/html/rfc1960
@@ -54,10 +55,47 @@
* Apache Celix filters can be used to match a set of Apache Celix properties and such Apache Celix filters should be
* used together with a set of properties.
*
- * Internally attribute values will be parsed to a long, double, boolean and Apache Celix version if
- * possible during creation. And these typed attribute values will be used in the to-be-matched property value,
- * if the property value is of the same type.
+ * #Filter matching and property value types
+ * When matching a filter the attribute type of a filter node is used to determine the type of the property value and
+ * this type is used to compare the property value with the filter attribute value.
+ * If the property value is of a type that the filter attribute value can be parsed to, the filter attribute value the
+ * comparison is done with the matching type. If the property value is not of a type that the filter attribute value
+ * can be parsed to, the comparison is done with the string representation of the property value.
*
+ * Internally attribute values will be parsed to a long, double, boolean, Apache Celix version and array list of
+ * longs, doubles, booleans, Apache Celix versions and string if possible during creation.
+ * And these typed attribute values will be used in the to-be-matched property value.
+ *
+ * Example: The filter "(key>20)" and a property set with a long value 3 for key "key", will match and the same
+ * filter but with a property set which has a string value "3" for key "key", will not match.
+ *
+ * #Filter matching and property value arrays
+ * If a filter matches a property set entry which has an array value (either a long, boolean, double, string or version
+ * array) the filter match will check if the array contains a single entry which matches the filter attribute value using
+ * the aforementioned "Filter matching and property value types" rules.
+ *
+ * Filter matching with array is supported for the following operands: "=" (including substring), ">=", "<=", ">", "<"
+ * and "~=".
+ *
+ * Example: The filter "(key=3)" will match the property set entry with key "key" and a long array list value of
+ * [1,2,3].
+ *
+ * #Substring filter operand
+ * The substring filter operand is used to match a string value with a filter attribute value. The filter attribute
+ * value can contain a `*` to match any character sequence. The filter attribute value can also contain a `*` at the
+ * start or end of the string to match any character sequence at the start or end of the string.
+ *
+ * A substring filter operand uses the string representation of the property value to match with the filter attribute
+ * or if the property value is an string array the substring filter operand will match if any of the string array values.
+ *
+ * Example: The filter "(key=*Smith)" will match the property set entry with key "key" and a string value "John Smith".
+ *
+ * #Approx filter operand
+ * The approx filter operand is used to check if the filter attribute value is a substring of the property value.
+ * A approx filter operand uses the string representation of the property value to match with the filter attribute or
+ * if the property value is an string array the approx filter operand will match if any of the string array values.
+ *
+ * Example: The filter "(key~=Smith)" will match the property set entry with key "key" and a string value "John Smith".
*/
#ifndef CELIX_FILTER_H_
diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c
index bad00bab..601ae499 100644
--- a/libs/utils/src/filter.c
+++ b/libs/utils/src/filter.c
@@ -515,40 +515,38 @@ static bool celix_filter_hasFilterChildren(celix_filter_t* filter) {
filter->operand == CELIX_FILTER_OPERAND_NOT;
}
+static void celix_filter_destroyInternal(celix_filter_internal_t* internal) {
+ if (internal) {
+ celix_version_destroy(internal->versionValue);
+ free(internal);
+ }
+}
+
+CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_internal_t, celix_filter_destroyInternal)
+
/**
* Compiles the filter, so that the attribute values are converted to the typed values if possible.
*/
static celix_status_t celix_filter_compile(celix_filter_t* filter) {
if (celix_filter_isCompareOperand(filter->operand)) {
- filter->internal = calloc(1, sizeof(*filter->internal));
- if (filter->internal == NULL) {
- celix_err_push("Filter Error: Failed to allocate memory.");
- return CELIX_ENOMEM;
+ celix_autoptr(celix_filter_internal_t) internal = calloc(1, sizeof(*internal));
+ if (!internal) {
+ return ENOMEM;
}
- do {
- filter->internal->longValue =
- celix_utils_convertStringToLong(filter->value, 0, &filter->internal->convertedToLong);
- if (filter->internal->convertedToLong) {
- break;
- }
- filter->internal->doubleValue =
- celix_utils_convertStringToDouble(filter->value, 0.0, &filter->internal->convertedToDouble);
- if (filter->internal->convertedToDouble) {
- break;
- }
- filter->internal->boolValue =
- celix_utils_convertStringToBool(filter->value, false, &filter->internal->convertedToBool);
- if (filter->internal->convertedToBool) {
- break;
- }
- celix_version_t* version;
- celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &version);
- if (convertStatus == CELIX_SUCCESS) {
- filter->internal->versionValue = version;
- filter->internal->convertedToVersion = true;
- break;
- }
- } while(false);
+ internal->longValue =
+ celix_utils_convertStringToLong(filter->value, 0, &internal->convertedToLong);
+ internal->doubleValue =
+ celix_utils_convertStringToDouble(filter->value, 0.0, &internal->convertedToDouble);
+ internal->boolValue =
+ celix_utils_convertStringToBool(filter->value, false, &internal->convertedToBool);
+
+ celix_status_t convertStatus = celix_utils_convertStringToVersion(filter->value, NULL, &internal->versionValue);
+ if (convertStatus == ENOMEM) {
+ return ENOMEM;
+ }
+ internal->convertedToVersion = convertStatus == CELIX_SUCCESS;
+
+ filter->internal = celix_steal_ptr(internal);
}
if (celix_filter_hasFilterChildren(filter)) {
@@ -602,68 +600,96 @@ static int celix_filter_cmpBool(bool a, bool b) {
}
}
-static int celix_filter_compareAttributeValue(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
- // not converted, fallback on string compare
- if (!filter->internal->convertedToLong && !filter->internal->convertedToDouble &&
- !filter->internal->convertedToBool && !filter->internal->convertedToVersion) {
- return strcmp(entry->value, filter->value);
+static bool celix_utils_convertCompareToBool(enum celix_filter_operand_enum op, int cmp) {
+ switch (op) {
+ case CELIX_FILTER_OPERAND_EQUAL:
+ return cmp == 0;
+ case CELIX_FILTER_OPERAND_GREATER:
+ return cmp > 0;
+ case CELIX_FILTER_OPERAND_GREATEREQUAL:
+ return cmp >= 0;
+ case CELIX_FILTER_OPERAND_LESS:
+ return cmp < 0;
+ case CELIX_FILTER_OPERAND_LESSEQUAL:
+ return cmp <= 0;
+ default:
+ //LCOV_EXCL_START
+ assert(false);
+ return false;
+ //LCOV_EXCL_STOP
}
+}
- // 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) {
- return celix_filter_cmpDouble(entry->typed.doubleValue, filter->internal->doubleValue);
- } else if (filter->internal->convertedToBool && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
- return celix_filter_cmpBool(entry->typed.boolValue, filter->internal->boolValue);
- } else if (filter->internal->convertedToVersion && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
- return celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue);
+static bool celix_utils_matchLongArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, long attributeValue) {
+ assert(list != NULL);
+ for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+ int cmp = celix_filter_cmpLong(celix_arrayList_getLong(list, i), attributeValue);
+ if (celix_utils_convertCompareToBool(op, cmp)) {
+ return true;
+ }
}
+ return false;
+}
- // 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);
- if (propertyConverted) {
- return celix_filter_cmpLong(val, filter->internal->longValue);
- }
- } else if (filter->internal->convertedToDouble) {
- double val = celix_utils_convertStringToDouble(entry->value, 0.0, &propertyConverted);
- if (propertyConverted) {
- return celix_filter_cmpDouble(val, filter->internal->doubleValue);
+static bool celix_utils_matchDoubleArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, double attributeValue) {
+ assert(list != NULL);
+ for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+ int cmp = celix_filter_cmpDouble(celix_arrayList_getDouble(list, i), attributeValue);
+ if (celix_utils_convertCompareToBool(op, cmp)) {
+ return true;
}
- } else if (filter->internal->convertedToBool) {
- bool val = celix_utils_convertStringToBool(entry->value, false, &propertyConverted);
- if (propertyConverted) {
- return celix_filter_cmpBool(val, filter->internal->boolValue);
+ }
+ return false;
+}
+
+
+static bool celix_utils_matchBoolArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, bool attributeValue) {
+ assert(list != NULL);
+ for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+ int cmp = celix_filter_cmpBool(celix_arrayList_getBool(list, i), attributeValue);
+ if (celix_utils_convertCompareToBool(op, cmp)) {
+ return true;
}
- } else if (filter->internal->convertedToVersion) {
- celix_version_t* val;
- celix_status_t convertStatus = celix_utils_convertStringToVersion(entry->value, NULL, &val);
- if (convertStatus == CELIX_SUCCESS) {
- int cmp = celix_version_compareTo(val, filter->internal->versionValue);
- celix_version_destroy(val);
- return cmp;
+ }
+ return false;
+}
+
+static bool celix_utils_matchVersionArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, celix_version_t* attributeValue) {
+ assert(list != NULL);
+ for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+ int cmp = celix_version_compareTo((celix_version_t*)celix_arrayList_get(list, i), attributeValue);
+ if (celix_utils_convertCompareToBool(op, cmp)) {
+ return true;
}
}
+ return false;
+}
- // fallback on string compare
- return strcmp(entry->value, filter->value);
+static bool celix_utils_matchStringArrays(enum celix_filter_operand_enum op, const celix_array_list_t* list, const char* attributeValue) {
+ assert(list != NULL);
+ for (int i = 0 ; i < celix_arrayList_size(list); ++i) {
+ int cmp = strcmp(celix_arrayList_getString(list, i), attributeValue);
+ if (celix_utils_convertCompareToBool(op, cmp)) {
+ return true;
+ }
+ }
+ return false;
}
-static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+
+static bool celix_filter_matchSubStringForValue(const celix_filter_t* filter, const char* value) {
assert(filter->children && celix_arrayList_size(filter->children) >= 2);
- size_t strLen = celix_utils_strlen(entry->value);
+ size_t strLen = celix_utils_strlen(value);
const char* initial = celix_arrayList_get(filter->children, 0);
const char* final = celix_arrayList_get(filter->children, celix_arrayList_size(filter->children) - 1);
- const char* currentValue = entry->value;
+ const char* currentValue = value;
if (initial) {
- const char* found = strstr(entry->value, initial);
+ const char* found = strstr(value, initial);
currentValue = found + celix_utils_strlen(initial);
- if (!found || found != entry->value) {
+ if (!found || found != value) {
return false;
}
}
@@ -679,7 +705,7 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi
if (final) {
const char* found = strstr(currentValue, final);
- if (!found || found + celix_utils_strlen(final) != entry->value + strLen) {
+ if (!found || found + celix_utils_strlen(final) != value + strLen) {
return false;
}
}
@@ -687,24 +713,73 @@ static bool celix_filter_matchSubString(const celix_filter_t* filter, const celi
return true;
}
+static bool celix_filter_matchSubString(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+ if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+ for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) {
+ const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i);
+ if (celix_filter_matchSubStringForValue(filter, substr)) {
+ return true;
+ }
+ return false;
+ }
+ }
+ return celix_filter_matchSubStringForValue(filter, entry->value);
+}
+
+static bool celix_filter_matchApprox(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
+ if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+ for (int i = 0; i < celix_arrayList_size(entry->typed.arrayValue); i++) {
+ const char* substr = celix_arrayList_getString(entry->typed.arrayValue, i);
+ if (strcasestr(substr, filter->value) != NULL) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return strcasestr(entry->value, filter->value) != NULL;
+}
+
static bool celix_filter_matchPropertyEntry(const celix_filter_t* filter, const celix_properties_entry_t* entry) {
- switch (filter->operand) {
- case CELIX_FILTER_OPERAND_SUBSTRING:
+ if (filter->operand == 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;
+ } else if (filter->operand == CELIX_FILTER_OPERAND_APPROX) {
+ return celix_filter_matchApprox(filter, entry);
+ }
+
+ assert(filter->operand == CELIX_FILTER_OPERAND_EQUAL || filter->operand == CELIX_FILTER_OPERAND_GREATER ||
+ filter->operand == CELIX_FILTER_OPERAND_LESS || filter->operand == CELIX_FILTER_OPERAND_GREATEREQUAL ||
+ filter->operand == CELIX_FILTER_OPERAND_LESSEQUAL);
+
+
+ //match for array types
+ if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG_ARRAY && filter->internal->convertedToLong) {
+ return celix_utils_matchLongArrays(filter->operand, entry->typed.arrayValue, filter->internal->longValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE_ARRAY && filter->internal->convertedToDouble) {
+ return celix_utils_matchDoubleArrays(filter->operand, entry->typed.arrayValue, filter->internal->doubleValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL_ARRAY && filter->internal->convertedToBool) {
+ return celix_utils_matchBoolArrays(filter->operand, entry->typed.arrayValue, filter->internal->boolValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION_ARRAY && filter->internal->convertedToVersion) {
+ return celix_utils_matchVersionArrays(filter->operand, entry->typed.arrayValue, filter->internal->versionValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING_ARRAY) {
+ return celix_utils_matchStringArrays(filter->operand, entry->typed.arrayValue, filter->value);
+ }
+
+ //regular compare -> match
+ int cmp;
+ if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG && filter->internal->convertedToLong) {
+ cmp = celix_filter_cmpLong(entry->typed.longValue, filter->internal->longValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE && filter->internal->convertedToDouble) {
+ cmp = celix_filter_cmpDouble(entry->typed.doubleValue, filter->internal->doubleValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL && filter->internal->convertedToBool) {
+ cmp = celix_filter_cmpBool(entry->typed.boolValue, filter->internal->boolValue);
+ } else if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION && filter->internal->convertedToVersion) {
+ cmp = celix_version_compareTo(entry->typed.versionValue, filter->internal->versionValue);
+ } else {
+ // type string or property type and converted filter attribute value do not match ->
+ // fallback on string compare
+ cmp = strcmp(entry->value, filter->value);
}
+ return celix_utils_convertCompareToBool(filter->operand, cmp);
}
celix_status_t filter_getString(celix_filter_t* filter, const char** filterStr) {
@@ -737,8 +812,9 @@ celix_filter_t* celix_filter_create(const char* filterString) {
celix_err_push("Filter Error: Extraneous trailing characters.");
return NULL;
}
- if (celix_filter_compile(filter) != CELIX_SUCCESS) {
- celix_err_push("Failed to compile filter");
+ celix_status_t compileStatus = celix_filter_compile(filter);
+ if (compileStatus != CELIX_SUCCESS) {
+ celix_err_pushf("Failed to compile filter: %s", celix_strerror(compileStatus));
return NULL;
}
filter->filterStr = celix_utils_strdup(filterString);
@@ -764,10 +840,7 @@ void celix_filter_destroy(celix_filter_t* filter) {
filter->attribute = NULL;
free((char*)filter->filterStr);
filter->filterStr = NULL;
- if (filter->internal != NULL) {
- celix_version_destroy(filter->internal->versionValue);
- free(filter->internal);
- }
+ celix_filter_destroyInternal(filter->internal);
free(filter);
}
@@ -807,10 +880,10 @@ bool celix_filter_match(const celix_filter_t* filter, const celix_properties_t*
return !childResult;
}
- // substring, equal, greater, greaterEqual, less, lessEqual, approx done with matchPropertyEntry
+ // present, 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 false;
}
return celix_filter_matchPropertyEntry(filter, entry);
}