PengZheng commented on code in PR #692:
URL: https://github.com/apache/celix/pull/692#discussion_r1413567641


##########
libs/utils/include/celix_filter.h:
##########
@@ -44,63 +89,121 @@ typedef enum celix_filter_operand_enum
     CELIX_FILTER_OPERAND_NOT,
 } celix_filter_operand_t;
 
+/**
+ * @brief Internal opaque struct for internal use only.
+ */
+typedef struct celix_filter_internal celix_filter_internal_t; // opaque struct 
for internal use only
 
-typedef struct celix_filter_internal celix_filter_internal_t; //opaque struct 
for internal use only
-
-typedef struct celix_filter_struct celix_filter_t;
-
-struct celix_filter_struct {
-    celix_filter_operand_t operand;
-    const char *attribute; //NULL for operands AND, OR ot NOT
-    const char *value; //NULL for operands AND, OR or NOT NOT
-    const char *filterStr;
+/**
+ * @brief The Apache Celix filter struct.
+ */
+typedef struct celix_filter_struct {
+    celix_filter_operand_t operand; /**< The filter operand. */
+    const char* attribute;          /**< The filter attribute; NULL for 
operands `AND`, `OR` or `NOT`. */
+    const char* value;              /**< The filter value; NULL for operands 
`AND`, `OR` or `NOT`. */
+    const char* filterStr;          /**< The filter string representation. */
 
-    //type is celix_filter_t* for AND, OR and NOT operator and char* for 
SUBSTRING
-    //for other operands children is NULL
-    celix_array_list_t *children;
+    celix_array_list_t* children; /**< The filter children; only valid if the 
operand is not `AND`, `OR` or `NOT` else

Review Comment:
   It seems that they can form something like typed union.



##########
documents/services.md:
##########
@@ -37,6 +37,45 @@ Also note that for Celix - in contrast with Java OSGi - it 
is only possible to r
 per service registration in the Apache Celix Framework. This restriction was 
added because C does not 
 (natively) supports multiple interfaces (struct with function pointers) on a 
single object/pointer.  
 
+## Service Properties
+As mentioned, a service is always registered with a set of properties 
(metadata). 
+The service properties are represented as a `celix_properties_t` for C and as 
a `celix::Properties` for C++.
+Service properties are used to classify and select services.
+
+Apache Celix properties store key/value pairs and the key is always a string, 
but the value can be a string,
+a long, a double, a bool or `celix_version_t` (`celix::Version` for C++). Next 
to the actual typed value a property
+entry always stores a string representation of the value.
+
+The following C functions can be used to create and manipulate properties:
+- `celix_properties_create` - Create a new properties object.
+- `celix_properties_destroy` - Destroy a properties object.
+- `celix_properties_set` - Set a string property value.
+- `celix_properties_setLong` - Set a long property value.
+- `celix_properties_setDouble` - Set a double property value.
+- `celix_properties_setBool` - Set a bool property value.
+- `celix_properties_setVersion` - Set a version property value.
+- `celix_properties_get` - Get a string or string representation of the 
property value.
+- `celix_properties_getAsLong` - Get a long property value or string value 
parsed as long.
+- `celix_properties_getAsDouble` - Get a double property value or string value 
parsed as double.
+- `celix_properties_getAsBool` - Get a bool property value or string value 
parsed as bool.
+- `celix_properties_getAsVersion` - Get a version property value or string 
value parsed as version.

Review Comment:
   `celix_properties_getAsVersion` will incur extra memory allocation. Instead, 
 we may advertise `celix_properties_getVersion` and make sure it is used in all 
efficiency-sensitive scenarios.



##########
libs/utils/include/celix_filter.h:
##########
@@ -44,63 +89,121 @@ typedef enum celix_filter_operand_enum
     CELIX_FILTER_OPERAND_NOT,
 } celix_filter_operand_t;
 
+/**
+ * @brief Internal opaque struct for internal use only.
+ */
+typedef struct celix_filter_internal celix_filter_internal_t; // opaque struct 
for internal use only
 
-typedef struct celix_filter_internal celix_filter_internal_t; //opaque struct 
for internal use only
-
-typedef struct celix_filter_struct celix_filter_t;
-
-struct celix_filter_struct {
-    celix_filter_operand_t operand;
-    const char *attribute; //NULL for operands AND, OR ot NOT
-    const char *value; //NULL for operands AND, OR or NOT NOT
-    const char *filterStr;
+/**
+ * @brief The Apache Celix filter struct.
+ */
+typedef struct celix_filter_struct {
+    celix_filter_operand_t operand; /**< The filter operand. */
+    const char* attribute;          /**< The filter attribute; NULL for 
operands `AND`, `OR` or `NOT`. */
+    const char* value;              /**< The filter value; NULL for operands 
`AND`, `OR` or `NOT`. */
+    const char* filterStr;          /**< The filter string representation. */
 
-    //type is celix_filter_t* for AND, OR and NOT operator and char* for 
SUBSTRING
-    //for other operands children is NULL
-    celix_array_list_t *children;
+    celix_array_list_t* children; /**< The filter children; only valid if the 
operand is not `AND`, `OR` or `NOT` else
+                                     the value is NULL. */
 
-    celix_filter_internal_t* internal; //for internal use only
-};
+    celix_filter_internal_t* internal; /**< Internal use only. */
+} celix_filter_t;
 
-CELIX_UTILS_EXPORT celix_filter_t* celix_filter_create(const char *filterStr);
+/**
+ * @brief Create a filter based on the provided filter string.
+ *
+ * If the return status is NULL, an error message is logged to celix_err.
+ *
+ * @param filterStr The filter string. 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);
 
-CELIX_UTILS_EXPORT void celix_filter_destroy(celix_filter_t *filter);
+/**
+ * @brief Destroy the provided filter. Ignores NULL values.
+ */
+CELIX_UTILS_EXPORT void celix_filter_destroy(celix_filter_t* filter);
 
+/**
+ * @brief Add support for `celix_autoptr`.
+ */
 CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_t, celix_filter_destroy)
 
-CELIX_UTILS_EXPORT bool celix_filter_match(const celix_filter_t *filter, const 
celix_properties_t* props);
+/**
+ * @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. If 
filter is NULL always returns true and
+ *         if props is NULL, the result is the same as if an empty properties 
set was provided.
+ */
+CELIX_UTILS_EXPORT bool celix_filter_match(const celix_filter_t* filter, const 
celix_properties_t* props);
 
-CELIX_UTILS_EXPORT bool celix_filter_matchFilter(const celix_filter_t 
*filter1, const celix_filter_t *filter2);
+/**
+ * @brief Check whether the 2 filters are equal.
+ *
+ * Note that the equals is done based on the parsed filter string and not on 
the provided filter strings.
+ * So the filters parsed from the strings "(key=value)" and " (key=value)" are 
equal.
+ *
+ * @param[in] filter1 The first filter.
+ * @param[in] filter2 The second filter.
+ * @return True if the filters are equal, false otherwise.
+ */
+CELIX_UTILS_EXPORT bool celix_filter_equals(const celix_filter_t* filter1, 
const celix_filter_t* filter2);
 
-CELIX_UTILS_EXPORT const char* celix_filter_getFilterString(const 
celix_filter_t *filter);
+/**
+ * @brief Get the filter string representation.
+ *
+ * @param[in] filter The filter.
+ * @return The filter string representation. The returned string is owned by 
the filter, must not be freed and is only
+ *         valid as long as the filter is valid.
+ */
+CELIX_UTILS_EXPORT const char* celix_filter_getFilterString(const 
celix_filter_t* filter);
 
 /**
- * Find the filter attribute.
- * @return The attribute value or NULL if the attribute is not found.
+ * @brief Find the filter attribute.
+ *
+ * @param[in] filter The filter.
+ * @param[in] attribute The attribute to find.
+ * @return The found attribute value or NULL if the attribute is not found. 
The returned string is owned by the filter,
+ *         must not be freed and is only valid as long as the filter is valid.
  */
-CELIX_UTILS_EXPORT const char* celix_filter_findAttribute(const celix_filter_t 
*filter, const char *attribute);
+CELIX_UTILS_EXPORT const char* celix_filter_findAttribute(const 
celix_filter_t* filter, const char* attribute);
 
 /**
- * Check whether the filter indicates the mandatory presence of an attribute 
with a specific value for the provided attribute key.
+ * @brief Check whether the filter indicates the mandatory presence of an 
attribute with a specific value for the
+ * provided attribute key.
  *
  * Example:
  *   using this function for attribute key "key1" on filter "(key1=value1)" 
yields true.
  *   using this function for attribute key "key1" on filter "(!(key1=value1))" 
yields false.
  *   using this function for attribute key "key1" on filter "(key1>=value1)" 
yields false.
  *   using this function for attribute key "key1" on filter 
"(|(key1=value1)(key2=value2))" yields false.
-*/
-CELIX_UTILS_EXPORT bool celix_filter_hasMandatoryEqualsValueAttribute(const 
celix_filter_t *filter, const char *attribute);
+ *
+ * @param[in] filter The filter.
+ * @param[in] attribute The attribute to check.
+ * @return True if the filter indicates the mandatory presence of an attribute 
with a specific value for the provided
+ *         attribute key, false otherwise.
+ */
+CELIX_UTILS_EXPORT bool celix_filter_hasMandatoryEqualsValueAttribute(const 
celix_filter_t* filter,
+                                                                      const 
char* attribute);
 
 /**
- * Chek whether the filter indicates the mandatory absence of an attribute, 
regardless of its value, for the provided attribute key.
+ * @brief Check whether the filter indicates the mandatory absence of an 
attribute, regardless of its value, for the
+ * provided attribute key.
  *
  * example:
  *   using this function for attribute key "key1" on the filter "(!(key1=*))" 
yields true.
  *   using this function for attribute key "key1" on the filter "(key1=*) 
yields false.
  *   using this function for attribute key "key1" on the filter "(key1=value)" 
yields false.
  *   using this function for attribute key "key1" on the filter 
"(|(!(key1=*))(key2=value2))" yields false.
+ *
+ * @param[in] filter The filter.
+ * @param[in] attribute The attribute to check.
+ * @return True if the filter indicates the mandatory absence of an attribute, 
regardless of its value, for the provided
+ *         attribute key, false otherwise.
  */
-CELIX_UTILS_EXPORT bool 
celix_filter_hasMandatoryNegatedPresenceAttribute(const celix_filter_t *filter, 
const char *attribute);
+CELIX_UTILS_EXPORT bool 
celix_filter_hasMandatoryNegatedPresenceAttribute(const celix_filter_t* filter,

Review Comment:
   This also suffers from lack of well-defined semantics.



##########
libs/utils/include/celix_filter.h:
##########
@@ -17,20 +17,65 @@
  * under the License.
  */
 
+/**
+ * @file celix_filter.h
+ * @brief Header file for the Celix Filter API.
+ *
+ * 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
+ *
+ * In short an Apache Celix filter is a tree of filter nodes, parsed from a 
filter string.
+ * Each node has an operand, an optional attribute string and an optional 
value string.
+ * The operand can be one of the following:
+ * - `=`: equal
+ * - `=*`: present
+ * - `~=`: approx
+ * - `>=`: greater
+ * - `<=`: less
+ * - `>=`: greater equal
+ * - `<=`: less equal
+ * - `=`: substring if string contains a `*`.
+ * - `!`: not
+ * - `&`: and
+ * - `|`: or
+ *
+ The filter string is a list of filter node strings wrapped in parenthesis to 
group filter nodes.
+ * The following are valid filter strings:
+ * - `(key=value)` # Matches if properties contains a entry with key "key" and 
value "value".
+ * - `(key>=2)`# Matches if properties contains a entry with key "key" and 
value greater or equal to 2.
+ * - `(key=*)` # Matches if properties contains a entry with key "key".
+ * - `(!(key=value))` # Matches if properties does not contain a entry with 
key "key" and value "value".
+ * - `(&(key1=value1)(key2=value2))` # Matches if properties contains a entry 
with key "key1" and value "value1" and
+ *                                   # a entry with key "key2" and value 
"value2".
+ * - `(|(key1=value1)(key2=value2))` # Matches if properties contains a entry 
with key "key1" and value "value1" or
+ *                                   # a entry with key "key2" and value 
"value2".
+ *
+ * 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 types attribute values will be used in 
the to-be-matched property value,

Review Comment:
   Typo? "these typed attribute values" or "these types"?



##########
libs/utils/include/celix_filter.h:
##########
@@ -44,63 +89,121 @@ typedef enum celix_filter_operand_enum
     CELIX_FILTER_OPERAND_NOT,
 } celix_filter_operand_t;
 
+/**
+ * @brief Internal opaque struct for internal use only.
+ */
+typedef struct celix_filter_internal celix_filter_internal_t; // opaque struct 
for internal use only
 
-typedef struct celix_filter_internal celix_filter_internal_t; //opaque struct 
for internal use only
-
-typedef struct celix_filter_struct celix_filter_t;
-
-struct celix_filter_struct {
-    celix_filter_operand_t operand;
-    const char *attribute; //NULL for operands AND, OR ot NOT
-    const char *value; //NULL for operands AND, OR or NOT NOT
-    const char *filterStr;
+/**
+ * @brief The Apache Celix filter struct.
+ */
+typedef struct celix_filter_struct {
+    celix_filter_operand_t operand; /**< The filter operand. */
+    const char* attribute;          /**< The filter attribute; NULL for 
operands `AND`, `OR` or `NOT`. */
+    const char* value;              /**< The filter value; NULL for operands 
`AND`, `OR` or `NOT`. */
+    const char* filterStr;          /**< The filter string representation. */
 
-    //type is celix_filter_t* for AND, OR and NOT operator and char* for 
SUBSTRING
-    //for other operands children is NULL
-    celix_array_list_t *children;
+    celix_array_list_t* children; /**< The filter children; only valid if the 
operand is not `AND`, `OR` or `NOT` else
+                                     the value is NULL. */
 
-    celix_filter_internal_t* internal; //for internal use only
-};
+    celix_filter_internal_t* internal; /**< Internal use only. */
+} celix_filter_t;
 
-CELIX_UTILS_EXPORT celix_filter_t* celix_filter_create(const char *filterStr);
+/**
+ * @brief Create a filter based on the provided filter string.
+ *
+ * If the return status is NULL, an error message is logged to celix_err.
+ *
+ * @param filterStr The filter string. 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);
 
-CELIX_UTILS_EXPORT void celix_filter_destroy(celix_filter_t *filter);
+/**
+ * @brief Destroy the provided filter. Ignores NULL values.
+ */
+CELIX_UTILS_EXPORT void celix_filter_destroy(celix_filter_t* filter);
 
+/**
+ * @brief Add support for `celix_autoptr`.
+ */
 CELIX_DEFINE_AUTOPTR_CLEANUP_FUNC(celix_filter_t, celix_filter_destroy)
 
-CELIX_UTILS_EXPORT bool celix_filter_match(const celix_filter_t *filter, const 
celix_properties_t* props);
+/**
+ * @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. If 
filter is NULL always returns true and
+ *         if props is NULL, the result is the same as if an empty properties 
set was provided.
+ */
+CELIX_UTILS_EXPORT bool celix_filter_match(const celix_filter_t* filter, const 
celix_properties_t* props);
 
-CELIX_UTILS_EXPORT bool celix_filter_matchFilter(const celix_filter_t 
*filter1, const celix_filter_t *filter2);
+/**
+ * @brief Check whether the 2 filters are equal.
+ *
+ * Note that the equals is done based on the parsed filter string and not on 
the provided filter strings.
+ * So the filters parsed from the strings "(key=value)" and " (key=value)" are 
equal.
+ *
+ * @param[in] filter1 The first filter.
+ * @param[in] filter2 The second filter.
+ * @return True if the filters are equal, false otherwise.
+ */
+CELIX_UTILS_EXPORT bool celix_filter_equals(const celix_filter_t* filter1, 
const celix_filter_t* filter2);
 
-CELIX_UTILS_EXPORT const char* celix_filter_getFilterString(const 
celix_filter_t *filter);
+/**
+ * @brief Get the filter string representation.
+ *
+ * @param[in] filter The filter.
+ * @return The filter string representation. The returned string is owned by 
the filter, must not be freed and is only
+ *         valid as long as the filter is valid.
+ */
+CELIX_UTILS_EXPORT const char* celix_filter_getFilterString(const 
celix_filter_t* filter);
 
 /**
- * Find the filter attribute.
- * @return The attribute value or NULL if the attribute is not found.
+ * @brief Find the filter attribute.
+ *
+ * @param[in] filter The filter.
+ * @param[in] attribute The attribute to find.
+ * @return The found attribute value or NULL if the attribute is not found. 
The returned string is owned by the filter,
+ *         must not be freed and is only valid as long as the filter is valid.
  */
-CELIX_UTILS_EXPORT const char* celix_filter_findAttribute(const celix_filter_t 
*filter, const char *attribute);
+CELIX_UTILS_EXPORT const char* celix_filter_findAttribute(const 
celix_filter_t* filter, const char* attribute);
 
 /**
- * Check whether the filter indicates the mandatory presence of an attribute 
with a specific value for the provided attribute key.
+ * @brief Check whether the filter indicates the mandatory presence of an 
attribute with a specific value for the
+ * provided attribute key.
  *
  * Example:
  *   using this function for attribute key "key1" on filter "(key1=value1)" 
yields true.
  *   using this function for attribute key "key1" on filter "(!(key1=value1))" 
yields false.
  *   using this function for attribute key "key1" on filter "(key1>=value1)" 
yields false.
  *   using this function for attribute key "key1" on filter 
"(|(key1=value1)(key2=value2))" yields false.
-*/
-CELIX_UTILS_EXPORT bool celix_filter_hasMandatoryEqualsValueAttribute(const 
celix_filter_t *filter, const char *attribute);
+ *
+ * @param[in] filter The filter.
+ * @param[in] attribute The attribute to check.
+ * @return True if the filter indicates the mandatory presence of an attribute 
with a specific value for the provided
+ *         attribute key, false otherwise.
+ */
+CELIX_UTILS_EXPORT bool celix_filter_hasMandatoryEqualsValueAttribute(const 
celix_filter_t* filter,

Review Comment:
   In `TEST_F(CxxFilterTestSuite, HasMandatoryEqualsValueAttribute)`, I found 
the following confusing:
   
   ```C++
       celix::Filter filter2{"(&(key1=value)(!(&(key=value)(!(key3=value)))))"};
       EXPECT_TRUE(filter2.hasMandatoryEqualsValueAttribute("key1"));
       EXPECT_FALSE(filter2.hasMandatoryEqualsValueAttribute("key2"));
       EXPECT_TRUE(filter2.hasMandatoryEqualsValueAttribute("key3"));
   
       celix::Properties props{};
       props.set("key1", "value");
       EXPECT_TRUE(filter2.match(props));
   
   ```
   
   If "key3" is mandatory, how could `filter2` matches `props` without "key3".
   
   The literal meaning of `hasMandatoryEqualsValueAttribute` seems to be that 
`filter2` should not match any properties that don't have "key3" or have "key3" 
with a value other than "value". If my understanding is correct, then this 
feature seems highly non-trivial to implement. For example, some complicated 
filters might always evaluate to be true/false. Without ability to do such 
deduction, the semantics of this interface is not well-defined.
   
   I'd like to consult the original author @jermus67 for more information.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@celix.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to