File content_filtered_topic.hpp

File List > astutedds > dcps > content_filtered_topic.hpp

Go to the documentation of this file


#ifndef ASTUTEDDS_DCPS_CONTENT_FILTERED_TOPIC_HPP
#define ASTUTEDDS_DCPS_CONTENT_FILTERED_TOPIC_HPP

#include <astutedds/dcps/qos.hpp>
#include <astutedds/dcps/topic_description.hpp>
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include <variant>
#include <regex>
#include <stdexcept>

namespace astutedds::dcps
{

    // Forward declarations
    class Topic;
    class DomainParticipant;

    using FilterValue = std::variant<int64_t, double, std::string, bool>;

    enum class FilterOperator
    {
        EQUAL,         // =
        NOT_EQUAL,     // != or <>
        LESS_THAN,     // <
        LESS_EQUAL,    // <=
        GREATER_THAN,  // >
        GREATER_EQUAL, // >=
        LIKE,          // LIKE (pattern matching)
        BETWEEN,       // BETWEEN ... AND ...
        IN,            // IN (value list)
        IS_NULL,       // IS NULL
        IS_NOT_NULL    // IS NOT NULL
    };

    enum class LogicalOperator
    {
        AND,
        OR,
        NOT
    };

    struct FilterCondition
    {
        std::string field_name;          
        FilterOperator op;               
        std::vector<FilterValue> values; 
        bool negated{false};             
    };

    struct FilterExpression;

    struct FilterExpressionNode
    {
        std::variant<
            FilterCondition,
            std::pair<LogicalOperator, std::vector<std::unique_ptr<FilterExpressionNode>>>>
            content;
    };

    class FilterExpression
    {
    public:
        FilterExpression() = default;

        explicit FilterExpression(const std::string &expression,
                                  const std::vector<std::string> &parameter_names = {});

        template <typename SampleType>
        bool evaluate(const SampleType &sample,
                      std::function<FilterValue(const SampleType &, const std::string &)> get_field) const;

        const std::string &expression_string() const { return expression_; }

        const std::vector<std::string> &parameters() const { return parameters_; }

        void set_parameter(size_t index, const std::string &value);

        bool is_valid() const { return valid_; }

        const std::string &error_message() const { return error_; }

    private:
        bool parse();
        bool parse_or_expression(size_t &pos);
        bool parse_and_expression(size_t &pos);
        bool parse_condition(size_t &pos);
        void skip_whitespace(size_t &pos);
        std::string parse_identifier(size_t &pos);
        FilterValue parse_value(size_t &pos);
        FilterOperator parse_operator(size_t &pos);

        bool evaluate_condition(const FilterCondition &cond,
                                const std::function<FilterValue(const std::string &)> &get_field) const;
        bool evaluate_node(const FilterExpressionNode &node,
                           const std::function<FilterValue(const std::string &)> &get_field) const;

        static bool compare_values(const FilterValue &lhs, FilterOperator op, const FilterValue &rhs);

    private:
        std::string expression_;
        std::vector<std::string> parameters_;
        std::unique_ptr<FilterExpressionNode> root_;
        bool valid_{false};
        std::string error_;
    };

    class ContentFilteredTopic : public TopicDescription
    {
    public:
        ContentFilteredTopic(const std::string &name,
                             std::shared_ptr<Topic> related_topic,
                             const std::string &filter_expression,
                             const std::vector<std::string> &expression_parameters = {});

        ContentFilteredTopic(const std::string &name,
                             Topic *related_topic,
                             const std::string &filter_expression,
                             const std::vector<std::string> &expression_parameters = {});

        // TopicDescription interface (const char* for PSM)
        const char *get_name() const override { return name_.c_str(); }
        const char *get_type_name() const override;

        const std::string &name() const { return name_; }

        const std::string &type_name() const;

        Topic *get_related_topic() const { return related_topic_raw_; }

        const std::string &get_filter_expression() const { return filter_expression_; }

        const std::vector<std::string> &get_expression_parameters() const
        {
            return expression_parameters_;
        }

        int set_expression_parameters(const std::vector<std::string> &parameters);

        template <typename SampleType>
        bool evaluate(const SampleType &sample,
                      std::function<FilterValue(const SampleType &, const std::string &)> get_field) const
        {
            return filter_.evaluate(sample, get_field);
        }

        bool is_filter_valid() const { return filter_.is_valid(); }

        const std::string &get_filter_error() const { return filter_.error_message(); }

    private:
        std::string name_;
        std::shared_ptr<Topic> related_topic_;
        Topic *related_topic_raw_{nullptr};  // non-null when constructed from raw ptr
        std::string filter_expression_;
        std::vector<std::string> expression_parameters_;
        FilterExpression filter_;
    };

    std::unique_ptr<ContentFilteredTopic> create_content_filtered_topic(
        DomainParticipant &participant,
        const std::string &name,
        std::shared_ptr<Topic> related_topic,
        const std::string &filter_expression,
        const std::vector<std::string> &expression_parameters = {});

    // ============================================================================
    // Template Implementation
    // ============================================================================

    template <typename SampleType>
    bool FilterExpression::evaluate(const SampleType &sample,
                                    std::function<FilterValue(const SampleType &, const std::string &)> get_field) const
    {
        if (!valid_ || !root_)
        {
            return true; // No filter or invalid filter passes all
        }

        auto field_getter = [&sample, &get_field](const std::string &field) -> FilterValue
        {
            return get_field(sample, field);
        };

        return evaluate_node(*root_, field_getter);
    }

} // namespace astutedds::dcps

#endif // ASTUTEDDS_DCPS_CONTENT_FILTERED_TOPIC_HPP