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 <[email protected]>
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);
 }

Reply via email to