From dc8f24f1a5066f394425227eb6ea7ec1c5966101 Mon Sep 17 00:00:00 2001 From: Chen Lihui Date: Mon, 27 Jun 2022 15:59:36 +0800 Subject: [PATCH] A fallback to support content filters in rcl Signed-off-by: Chen Lihui --- CONTRIBUTING.md | 18 + LICENSE | 202 ++++++++ README.md | 9 + common_content_filter/CMakeLists.txt | 100 ++++ common_content_filter/README.md | 2 + .../include/common_content_filter/api.h | 99 ++++ .../visibility_control.h | 58 +++ common_content_filter/package.xml | 30 ++ .../src/FilterCompoundCondition.cpp | 101 ++++ .../src/FilterCompoundCondition.hpp | 78 +++ common_content_filter/src/FilterCondition.hpp | 127 +++++ .../src/FilterConditionState.hpp | 46 ++ .../src/FilterEmptyExpression.hpp | 48 ++ .../src/FilterExpression.cpp | 61 +++ .../src/FilterExpression.hpp | 63 +++ .../src/FilterExpressionParser.cpp | 146 ++++++ .../src/FilterExpressionParser.hpp | 64 +++ .../src/FilterExpressionParserImpl/Readme.md | 7 + .../identifiers.hpp | 189 +++++++ .../literal_values.hpp | 70 +++ .../FilterExpressionParserImpl/parameters.hpp | 43 ++ .../FilterExpressionParserImpl/rearrange.hpp | 76 +++ common_content_filter/src/FilterFactory.cpp | 470 ++++++++++++++++++ common_content_filter/src/FilterFactory.hpp | 90 ++++ common_content_filter/src/FilterField.cpp | 202 ++++++++ common_content_filter/src/FilterField.hpp | 155 ++++++ common_content_filter/src/FilterGrammar.hpp | 126 +++++ common_content_filter/src/FilterParameter.cpp | 48 ++ common_content_filter/src/FilterParameter.hpp | 52 ++ common_content_filter/src/FilterParseNode.hpp | 70 +++ common_content_filter/src/FilterPredicate.cpp | 99 ++++ common_content_filter/src/FilterPredicate.hpp | 91 ++++ common_content_filter/src/FilterValue.cpp | 356 +++++++++++++ common_content_filter/src/FilterValue.hpp | 224 +++++++++ common_content_filter/src/IContentFilter.hpp | 45 ++ .../src/IContentFilterFactory.hpp | 91 ++++ common_content_filter/src/Log.hpp | 33 ++ common_content_filter/src/ObjectPool.hpp | 98 ++++ common_content_filter/src/Utilities.cpp | 55 ++ common_content_filter/src/Utilities.hpp | 24 + common_content_filter/src/api.cpp | 369 ++++++++++++++ common_content_filter/test/test_api.cpp | 449 +++++++++++++++++ tao_pegtl_vendor/CMakeLists.txt | 80 +++ tao_pegtl_vendor/CONTRIBUTING.md | 18 + tao_pegtl_vendor/LICENSE | 201 ++++++++ tao_pegtl_vendor/README.md | 2 + tao_pegtl_vendor/package.xml | 24 + .../tao_pegtl_vendor-extras.cmake | 18 + test_content_filter_msgs/CMakeLists.txt | 25 + test_content_filter_msgs/README.md | 2 + test_content_filter_msgs/msg/Basic.msg | 4 + test_content_filter_msgs/msg/Complex.msg | 2 + test_content_filter_msgs/msg/Intermediate.msg | 2 + test_content_filter_msgs/package.xml | 22 + 54 files changed, 5184 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 common_content_filter/CMakeLists.txt create mode 100644 common_content_filter/README.md create mode 100644 common_content_filter/include/common_content_filter/api.h create mode 100644 common_content_filter/include/common_content_filter/visibility_control.h create mode 100644 common_content_filter/package.xml create mode 100644 common_content_filter/src/FilterCompoundCondition.cpp create mode 100644 common_content_filter/src/FilterCompoundCondition.hpp create mode 100644 common_content_filter/src/FilterCondition.hpp create mode 100644 common_content_filter/src/FilterConditionState.hpp create mode 100644 common_content_filter/src/FilterEmptyExpression.hpp create mode 100644 common_content_filter/src/FilterExpression.cpp create mode 100644 common_content_filter/src/FilterExpression.hpp create mode 100644 common_content_filter/src/FilterExpressionParser.cpp create mode 100644 common_content_filter/src/FilterExpressionParser.hpp create mode 100644 common_content_filter/src/FilterExpressionParserImpl/Readme.md create mode 100644 common_content_filter/src/FilterExpressionParserImpl/identifiers.hpp create mode 100644 common_content_filter/src/FilterExpressionParserImpl/literal_values.hpp create mode 100644 common_content_filter/src/FilterExpressionParserImpl/parameters.hpp create mode 100644 common_content_filter/src/FilterExpressionParserImpl/rearrange.hpp create mode 100644 common_content_filter/src/FilterFactory.cpp create mode 100644 common_content_filter/src/FilterFactory.hpp create mode 100644 common_content_filter/src/FilterField.cpp create mode 100644 common_content_filter/src/FilterField.hpp create mode 100644 common_content_filter/src/FilterGrammar.hpp create mode 100644 common_content_filter/src/FilterParameter.cpp create mode 100644 common_content_filter/src/FilterParameter.hpp create mode 100644 common_content_filter/src/FilterParseNode.hpp create mode 100644 common_content_filter/src/FilterPredicate.cpp create mode 100644 common_content_filter/src/FilterPredicate.hpp create mode 100644 common_content_filter/src/FilterValue.cpp create mode 100644 common_content_filter/src/FilterValue.hpp create mode 100644 common_content_filter/src/IContentFilter.hpp create mode 100644 common_content_filter/src/IContentFilterFactory.hpp create mode 100644 common_content_filter/src/Log.hpp create mode 100644 common_content_filter/src/ObjectPool.hpp create mode 100644 common_content_filter/src/Utilities.cpp create mode 100644 common_content_filter/src/Utilities.hpp create mode 100644 common_content_filter/src/api.cpp create mode 100644 common_content_filter/test/test_api.cpp create mode 100644 tao_pegtl_vendor/CMakeLists.txt create mode 100644 tao_pegtl_vendor/CONTRIBUTING.md create mode 100644 tao_pegtl_vendor/LICENSE create mode 100644 tao_pegtl_vendor/README.md create mode 100644 tao_pegtl_vendor/package.xml create mode 100644 tao_pegtl_vendor/tao_pegtl_vendor-extras.cmake create mode 100644 test_content_filter_msgs/CMakeLists.txt create mode 100644 test_content_filter_msgs/README.md create mode 100644 test_content_filter_msgs/msg/Basic.msg create mode 100644 test_content_filter_msgs/msg/Complex.msg create mode 100644 test_content_filter_msgs/msg/Intermediate.msg create mode 100644 test_content_filter_msgs/package.xml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cfba094 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,18 @@ +Any contribution that you make to this repository will +be under the Apache 2 License, as dictated by that +[license](http://www.apache.org/licenses/LICENSE-2.0.html): + +~~~ +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. +~~~ + +Contributors must sign-off each commit by adding a `Signed-off-by: ...` +line to commit messages to certify that they have the right to submit +the code they are contributing to the project according to the +[Developer Certificate of Origin (DCO)](https://developercertificate.org/). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..08b98b2 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# common_content_filter + +This repository contains packages to support content filter fallback used in rcl. + + - [common_content_filter](common_content_filter/README.md) + + - [tao_pegtl_vendor](tao_pegtl_vendor/README.md) + + - [test_content_filter_msgs](test_content_filter_msgs/README.md) diff --git a/common_content_filter/CMakeLists.txt b/common_content_filter/CMakeLists.txt new file mode 100644 index 0000000..4d1db23 --- /dev/null +++ b/common_content_filter/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.8) +project(common_content_filter) + + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rcutils REQUIRED) +find_package(rcpputils REQUIRED) +find_package(rmw REQUIRED) +find_package(rmw_implementation REQUIRED) +find_package(rosidl_typesupport_introspection_c REQUIRED) +find_package(rosidl_typesupport_introspection_cpp REQUIRED) +find_package(tao_pegtl_vendor REQUIRED) + +add_library(${PROJECT_NAME}_library SHARED + src/api.cpp + src/FilterCompoundCondition.cpp + src/FilterExpression.cpp + src/FilterExpressionParser.cpp + src/FilterFactory.cpp + src/FilterField.cpp + src/FilterParameter.cpp + src/FilterPredicate.cpp + src/FilterValue.cpp + src/Utilities.cpp) + +set_target_properties(${PROJECT_NAME}_library + PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) + +target_include_directories(${PROJECT_NAME}_library + PUBLIC + "$" + "$") + +ament_target_dependencies(${PROJECT_NAME}_library + "rcpputils" + "rcutils" + "rmw" + "rmw_implementation" + "rosidl_typesupport_introspection_c" + "rosidl_typesupport_introspection_cpp" + "tao_pegtl_vendor" +) + +# Causes the visibility macros to use dllexport rather than dllimport, +# which is appropriate when building the dll but not consuming it. +target_compile_definitions(${PROJECT_NAME}_library + PRIVATE "COMMON_CONTENT_FILTER_BUILDING_LIBRARY") + +install( + TARGETS ${PROJECT_NAME}_library EXPORT ${PROJECT_NAME}_library + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +# Export old-style CMake variables +ament_export_include_directories("include/${PROJECT_NAME}") +ament_export_libraries(${PROJECT_NAME}_library) + +# Export modern CMake targets +ament_export_targets(${PROJECT_NAME}_library) + +ament_export_dependencies(ament_cmake_core) +ament_export_dependencies(rcpputils) +ament_export_dependencies(rcutils) +ament_export_dependencies(rmw) +ament_export_dependencies(rmw_implementation) +ament_export_dependencies(rosidl_typesupport_introspection_c) +ament_export_dependencies(rosidl_typesupport_introspection_cpp) +ament_export_dependencies(tao_pegtl_vendor) + +install( + DIRECTORY include/ + DESTINATION include/${PROJECT_NAME}) + +if(BUILD_TESTING) + find_package(ament_cmake_gtest REQUIRED) + find_package(ament_lint_auto REQUIRED) + find_package(test_content_filter_msgs REQUIRED) + find_package(test_msgs REQUIRED) + find_package(osrf_testing_tools_cpp REQUIRED) + + ament_lint_auto_find_test_dependencies() + + ament_add_gtest(test_api test/test_api.cpp) + target_link_libraries(test_api ${PROJECT_NAME}_library) + ament_target_dependencies( + test_api test_content_filter_msgs test_msgs osrf_testing_tools_cpp) +endif() + +ament_package() diff --git a/common_content_filter/README.md b/common_content_filter/README.md new file mode 100644 index 0000000..ad00969 --- /dev/null +++ b/common_content_filter/README.md @@ -0,0 +1,2 @@ +# common_content_filter +To support the content topic filter (a CFT fallback on the DDS) in the reader side used in rcl. diff --git a/common_content_filter/include/common_content_filter/api.h b/common_content_filter/include/common_content_filter/api.h new file mode 100644 index 0000000..f3c05bf --- /dev/null +++ b/common_content_filter/include/common_content_filter/api.h @@ -0,0 +1,99 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +#ifndef COMMON_CONTENT_FILTER__API_H_ +#define COMMON_CONTENT_FILTER__API_H_ + +#include +#include +#include + +#include "common_content_filter/visibility_control.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/// Create a common content filter instance to filter data. +/** + * \param[in] type_support Type support of the topic data being filtered + * \return a valid address if success, or NULL on failure + */ +COMMON_CONTENT_FILTER_PUBLIC +void * +common_content_filter_create(const rosidl_message_type_support_t * type_support); + +/// Check if the content filter instance is enabled. +/** + * \param[in] instance The content filter instance + * \return true if enabled, or false + */ +COMMON_CONTENT_FILTER_PUBLIC +bool +common_content_filter_is_enabled(void * instance); + +/// Use the content filter instance to evalute the data. +/** + * \param[in] instance The content filter instance + * \param[in] data The ros2 payload + * \param[in] serialized Indicate the ros2 payload is serialized or not + * \return true if evaluate successfully, or false + */ +COMMON_CONTENT_FILTER_PUBLIC +bool +common_content_filter_evaluate(void * instance, void * data, bool serialized); + +/// Set a common content filter instance with an options. +/** + * \param[in] instance The content filter instance + * \param[in] options the filter options + * \return true if success, or false + */ +COMMON_CONTENT_FILTER_PUBLIC +bool +common_content_filter_set( + void * instance, + const rmw_subscription_content_filter_options_t * options +); + +/// Get the options from a common content filter instance. +/** + * \param[in] instance The content filter instance + * \param[in] allocator Type support of the topic data being filtered + * \param[in] options the filter options + * \return true if success, or false + */ +COMMON_CONTENT_FILTER_PUBLIC +bool +common_content_filter_get( + void * instance, + rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options +); + +/// Destroy the content filter instance. +/** + * \param[in] instance The content filter instance + */ +COMMON_CONTENT_FILTER_PUBLIC +void +common_content_filter_destroy(void * instance); + +#ifdef __cplusplus +} +#endif + +#endif // COMMON_CONTENT_FILTER__API_H_ diff --git a/common_content_filter/include/common_content_filter/visibility_control.h b/common_content_filter/include/common_content_filter/visibility_control.h new file mode 100644 index 0000000..77a0065 --- /dev/null +++ b/common_content_filter/include/common_content_filter/visibility_control.h @@ -0,0 +1,58 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +#ifndef COMMON_CONTENT_FILTER__VISIBILITY_CONTROL_H_ +#define COMMON_CONTENT_FILTER__VISIBILITY_CONTROL_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +// This logic was borrowed (then namespaced) from the examples on the gcc wiki: +// https://gcc.gnu.org/wiki/Visibility + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define COMMON_CONTENT_FILTER_EXPORT __attribute__ ((dllexport)) + #define COMMON_CONTENT_FILTER_IMPORT __attribute__ ((dllimport)) + #else + #define COMMON_CONTENT_FILTER_EXPORT __declspec(dllexport) + #define COMMON_CONTENT_FILTER_IMPORT __declspec(dllimport) + #endif + #ifdef COMMON_CONTENT_FILTER_BUILDING_LIBRARY + #define COMMON_CONTENT_FILTER_PUBLIC COMMON_CONTENT_FILTER_EXPORT + #else + #define COMMON_CONTENT_FILTER_PUBLIC COMMON_CONTENT_FILTER_IMPORT + #endif + #define COMMON_CONTENT_FILTER_PUBLIC_TYPE COMMON_CONTENT_FILTER_PUBLIC + #define COMMON_CONTENT_FILTER_LOCAL +#else + #define COMMON_CONTENT_FILTER_EXPORT __attribute__ ((visibility("default"))) + #define COMMON_CONTENT_FILTER_IMPORT + #if __GNUC__ >= 4 + #define COMMON_CONTENT_FILTER_PUBLIC __attribute__ ((visibility("default"))) + #define COMMON_CONTENT_FILTER_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define COMMON_CONTENT_FILTER_PUBLIC + #define COMMON_CONTENT_FILTER_LOCAL + #endif + #define COMMON_CONTENT_FILTER_PUBLIC_TYPE +#endif + +#ifdef __cplusplus +} +#endif + +#endif // COMMON_CONTENT_FILTER__VISIBILITY_CONTROL_H_ diff --git a/common_content_filter/package.xml b/common_content_filter/package.xml new file mode 100644 index 0000000..ea8e45c --- /dev/null +++ b/common_content_filter/package.xml @@ -0,0 +1,30 @@ + + + + common_content_filter + 0.0.1 + To support the common content filter (a CFT fallback on the DDS) used in rcl. + chenlh + Apache License 2.0 + + ament_cmake + + rcutils + rcpputils + rmw + rmw_implementation + rosidl_typesupport_introspection_c + rosidl_typesupport_introspection_cpp + tao_pegtl_vendor + + ament_cmake_gtest + ament_lint_auto + ament_lint_common + test_content_filter_msgs + test_msgs + osrf_testing_tools_cpp + + + ament_cmake + + diff --git a/common_content_filter/src/FilterCompoundCondition.cpp b/common_content_filter/src/FilterCompoundCondition.cpp new file mode 100644 index 0000000..da852c2 --- /dev/null +++ b/common_content_filter/src/FilterCompoundCondition.cpp @@ -0,0 +1,101 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterCompoundCondition.cpp + */ + +#include "FilterCompoundCondition.hpp" + +#include +#include +#include + +#include "FilterCondition.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +FilterCompoundCondition::FilterCompoundCondition( + OperationKind op, + std::unique_ptr && left, + std::unique_ptr && right) +: op_(op) + , left_(std::move(left)) + , right_(std::move(right)) +{ + assert(left_); + assert(right_ || OperationKind::NOT == op_); + + left_->set_parent(this); + if (right_) { + right_->set_parent(this); + } +} + +void FilterCompoundCondition::propagate_reset() noexcept +{ + num_children_decided_ = 0; + + left_->reset(); + if (right_) { + right_->reset(); + } +} + +void FilterCompoundCondition::child_has_changed( + const FilterCondition & child) noexcept +{ + FilterConditionState child_state = child.get_state(); + assert(FilterConditionState::UNDECIDED != child_state); + + ++num_children_decided_; + + if (FilterConditionState::UNDECIDED == get_state()) { + switch (op_) { + case OperationKind::NOT: + set_result(FilterConditionState::RESULT_FALSE == child_state); + break; + + case OperationKind::AND: + if (FilterConditionState::RESULT_FALSE == child_state) { + set_result(false); + } else { + if (2 == num_children_decided_) { + set_result(true); + } + } + break; + + case OperationKind::OR: + if (FilterConditionState::RESULT_TRUE == child_state) { + set_result(true); + } else { + if (2 == num_children_decided_) { + set_result(false); + } + } + break; + + default: + assert(false); + } + } +} + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterCompoundCondition.hpp b/common_content_filter/src/FilterCompoundCondition.hpp new file mode 100644 index 0000000..c1ede6f --- /dev/null +++ b/common_content_filter/src/FilterCompoundCondition.hpp @@ -0,0 +1,78 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterCompoundCondition.hpp + */ + +#ifndef FILTERCOMPOUNDCONDITION_HPP_ +#define FILTERCOMPOUNDCONDITION_HPP_ + +#include + +#include "FilterCondition.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * A FilterCondition that performs a logical operation over one or two FilterCondition objects. + */ +class FilterCompoundCondition final : public FilterCondition +{ +public: + /** + * Possible kinds of logical operations + */ + enum class OperationKind : uint8_t + { + NOT, ///< NOT left + AND, ///< left AND right + OR ///< left OR right + }; + + /** + * Construct a FilterCompoundCondition. + * + * @param[in] op Operation to perform. + * @param[in] left Left operand. + * @param[in] right Right operand. + */ + FilterCompoundCondition( + OperationKind op, + std::unique_ptr && left, + std::unique_ptr && right); + + virtual ~FilterCompoundCondition() = default; + +protected: + void propagate_reset() noexcept final; + + void child_has_changed( + const FilterCondition & child) noexcept final; + +private: + OperationKind op_; + std::unique_ptr left_; + std::unique_ptr right_; + uint8_t num_children_decided_ = 0; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERCOMPOUNDCONDITION_HPP_ diff --git a/common_content_filter/src/FilterCondition.hpp b/common_content_filter/src/FilterCondition.hpp new file mode 100644 index 0000000..b1a60fd --- /dev/null +++ b/common_content_filter/src/FilterCondition.hpp @@ -0,0 +1,127 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterCondition.hpp + */ + +#ifndef FILTERCONDITION_HPP_ +#define FILTERCONDITION_HPP_ + +#include "FilterConditionState.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * Base class for conditions on a filter expression. + */ +class FilterCondition +{ +public: + friend class FilterCompoundCondition; + + virtual ~FilterCondition() = default; + + /** + * @return the current state of this condition. + */ + inline FilterConditionState get_state() const noexcept + { + return state_; + } + + /** + * Instruct this condition to reset. + * Will propagate the reset command down the expression tree. + * + * @post The state of this condition will be UNDECIDED. + */ + inline void reset() noexcept + { + state_ = FilterConditionState::UNDECIDED; + propagate_reset(); + } + +protected: + /** + * Set a new state for this condition. + * May propagate the change up the expression tree by calling + * @ref child_has_changed on the parent of this condition. + * + * @param[in] state New state for this condition. + * + * @post The state of this condition will be @c state. + */ + inline void set_state( + FilterConditionState state) noexcept + { + if (state != state_) { + state_ = state; + if (nullptr != parent_) { + parent_->child_has_changed(*this); + } + } + } + + /** + * Set the result of this condition. + * + * @param[in] result The result to be set. + * + * @post The state of this condition will not be UNDECIDED. + */ + inline void set_result( + bool result) noexcept + { + set_state(result ? FilterConditionState::RESULT_TRUE : FilterConditionState::RESULT_FALSE); + } + + /** + * Set a new parent for this condition. + * + * @param parent New parent to set. + */ + inline void set_parent( + FilterCondition * parent) noexcept + { + parent_ = parent; + } + + /** + * Propagates the reset command down the expression tree. + */ + virtual void propagate_reset() noexcept = 0; + + + /** + * A child condition will call this method whenever its state is changed. + * + * @param[in] child The child condition + */ + virtual void child_has_changed( + const FilterCondition & child) noexcept = 0; + +private: + FilterConditionState state_ = FilterConditionState::UNDECIDED; + FilterCondition * parent_ = nullptr; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERCONDITION_HPP_ diff --git a/common_content_filter/src/FilterConditionState.hpp b/common_content_filter/src/FilterConditionState.hpp new file mode 100644 index 0000000..d0443cf --- /dev/null +++ b/common_content_filter/src/FilterConditionState.hpp @@ -0,0 +1,46 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterConditionState.hpp + */ + +#ifndef FILTERCONDITIONSTATE_HPP_ +#define FILTERCONDITIONSTATE_HPP_ + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * Possible states of a FilterCondition. + */ +enum class FilterConditionState : char +{ + /// Initial state of the FilterCondition, indicating there is no result. + UNDECIDED, + + /// State indicating that the FilterCondition evaluates to @c false. + RESULT_FALSE, + + /// State indicating that the FilterCondition evaluates to @c true. + RESULT_TRUE +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERCONDITIONSTATE_HPP_ diff --git a/common_content_filter/src/FilterEmptyExpression.hpp b/common_content_filter/src/FilterEmptyExpression.hpp new file mode 100644 index 0000000..b482d95 --- /dev/null +++ b/common_content_filter/src/FilterEmptyExpression.hpp @@ -0,0 +1,48 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterEmptyExpression.hpp + */ + +#ifndef FILTEREMPTYEXPRESSION_HPP_ +#define FILTEREMPTYEXPRESSION_HPP_ + +#include "IContentFilter.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * An IContentFilter for empty expressions that always evaluates to true. + */ +class FilterEmptyExpression final : public IContentFilter +{ +public: + bool evaluate( + const void * payload) const final + { + static_cast(payload); + + return true; + } +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTEREMPTYEXPRESSION_HPP_ diff --git a/common_content_filter/src/FilterExpression.cpp b/common_content_filter/src/FilterExpression.cpp new file mode 100644 index 0000000..eb289f1 --- /dev/null +++ b/common_content_filter/src/FilterExpression.cpp @@ -0,0 +1,61 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterExpression.cpp + */ + +#include "FilterExpression.hpp" + +#include +#include +#include +#include + +#include "FilterCondition.hpp" +#include "FilterField.hpp" +#include "FilterParameter.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +bool FilterExpression::evaluate( + const void * payload) const +{ + root->reset(); + for (auto it = fields.begin(); + it != fields.end() && FilterConditionState::UNDECIDED == root->get_state(); + ++it) + { + if (!it->second->set_value(payload)) { + return false; + } + } + + return FilterConditionState::RESULT_TRUE == root->get_state(); +} + +void FilterExpression::clear() +{ + parameters.clear(); + fields.clear(); + root.reset(); +} + + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterExpression.hpp b/common_content_filter/src/FilterExpression.hpp new file mode 100644 index 0000000..017f4e5 --- /dev/null +++ b/common_content_filter/src/FilterExpression.hpp @@ -0,0 +1,63 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterExpression.hpp + */ + +#ifndef FILTEREXPRESSION_HPP_ +#define FILTEREXPRESSION_HPP_ + +#include +#include +#include +#include + +#include "IContentFilter.hpp" +#include "FilterCondition.hpp" +#include "FilterField.hpp" +#include "FilterParameter.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * An IContentFilter that evaluates -SQL filter expressions + */ +class FilterExpression final : public IContentFilter +{ +public: + bool evaluate( + const void * payload) const final; + + /** + * Clear the information held by this object. + */ + void clear(); + + /// The root condition of the expression tree. + std::unique_ptr root; + /// The fields referenced by this expression. + std::map> fields; + /// The parameters referenced by this expression. + std::vector> parameters; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTEREXPRESSION_HPP_ diff --git a/common_content_filter/src/FilterExpressionParser.cpp b/common_content_filter/src/FilterExpressionParser.cpp new file mode 100644 index 0000000..e18613d --- /dev/null +++ b/common_content_filter/src/FilterExpressionParser.cpp @@ -0,0 +1,146 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterExpressionParser.cpp + */ + +#include "FilterExpressionParser.hpp" + +// header files needed by identifiers.hpp +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "FilterGrammar.hpp" +#include "FilterParseNode.hpp" +#include "FilterValue.hpp" +#include "FilterField.hpp" +#include "Log.hpp" +#include "Utilities.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ +namespace parser +{ + +using namespace tao::TAO_PEGTL_NAMESPACE; // NOLINT + +#include "FilterExpressionParserImpl/rearrange.hpp" +#include "FilterExpressionParserImpl/literal_values.hpp" +#include "FilterExpressionParserImpl/identifiers.hpp" +#include "FilterExpressionParserImpl/parameters.hpp" + +// select which rules in the grammar will produce parse tree nodes: +template +using selector = parse_tree::selector< + Rule, + literal_value_processor::on< + true_value, + false_value, + hex_value, + integer_value, + float_value, + char_value, + string_value>, + parameter_processor::on< + parameter_value>, + parse_tree::store_content::on< + string_content, + integer, + index_part, + identifier>, + parse_tree::remove_content::on< + eq_op, + gt_op, + ge_op, + lt_op, + le_op, + ne_op, + like_op, + match_op, + and_op, + or_op, + not_op, + dot_op, + between_op, + not_between_op>, + rearrange::on< + boolean_value, + ComparisonPredicate, + BetweenPredicate, + Range, + Condition, + ConditionList>, + identifier_processor::on< + fieldname_part, + fieldname> +>; + +std::unique_ptr parse_filter_expression( + const char * expression, + const rosidl_message_type_support_t * type_support) +{ + memory_input<> in(expression, ""); + try { + CurrentIdentifierState identifier_state {type_support, nullptr, 0, {}}; + return parse_tree::parse(in, identifier_state); + } catch (const parse_error & e) { + const auto p = e.positions.front(); + logError( + SQLFILTER, "PARSE ERROR: " << e.what() << std::endl + << in.line_at(p) << std::endl + << std::string(p.byte_in_line, ' ') << '^'); + } catch (const std::exception & e) { + logError(SQLFILTER, "ERROR '" << e.what() << "' while parsing " << expression); + } + + return nullptr; +} + +std::unique_ptr parse_literal_value( + const char * expression) +{ + memory_input<> in(expression, ""); + try { + CurrentIdentifierState identifier_state{nullptr, nullptr, 0, {}}; + return parse_tree::parse(in, identifier_state); + } catch (const parse_error & e) { + const auto p = e.positions.front(); + logError( + SQLFILTER, "PARSE ERROR: " << e.what() << std::endl + << in.line_at(p) << std::endl + << std::string(p.byte_in_line, ' ') << '^'); + } catch (const std::exception & e) { + logError(SQLFILTER, "ERROR '" << e.what() << "' while parsing " << expression); + } + + return nullptr; +} + +} // namespace parser +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterExpressionParser.hpp b/common_content_filter/src/FilterExpressionParser.hpp new file mode 100644 index 0000000..c8fbbe7 --- /dev/null +++ b/common_content_filter/src/FilterExpressionParser.hpp @@ -0,0 +1,64 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterExpressionParser.hpp + */ + +#ifndef FILTEREXPRESSIONPARSER_HPP_ +#define FILTEREXPRESSIONPARSER_HPP_ + +#include +#include +#include "FilterParseNode.hpp" + + +namespace common_content_filter +{ +namespace SQLFilter +{ +namespace parser +{ + +/** + * Performs parsing of a string containing a -SQL filter expression. + * + * @param[in] expression The string to parse. + * @param[in] type_support The type support representing the type of the data being filtered. + * + * @return nullptr when there is a parsing error. + * @return A pointer to the root node of the AST tree for the expression. + */ +std::unique_ptr parse_filter_expression( + const char * expression, + const rosidl_message_type_support_t * type_support); + +/** + * Performs parsing of a string containing a literal value. + * This method is used to perform parsing of parameter values. + * + * @param[in] value The string to parse. + * + * @return nullptr when there is a parsing error. + * @return A simple tree consisting of a root node, with a single child that contains the generated FilterValue. + */ +std::unique_ptr parse_literal_value( + const char * value); + +} // namespace parser +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTEREXPRESSIONPARSER_HPP_ diff --git a/common_content_filter/src/FilterExpressionParserImpl/Readme.md b/common_content_filter/src/FilterExpressionParserImpl/Readme.md new file mode 100644 index 0000000..0251df6 --- /dev/null +++ b/common_content_filter/src/FilterExpressionParserImpl/Readme.md @@ -0,0 +1,7 @@ +## Internal support classes for DataReaderImpl + +This folder contains the declaration of several internal support classes to improve +maintenance and readability of FilterExpressionParser.hpp. + +They are not meant to be used outside FilterExpressionParser and their functionality should be +fully covered by the unit tests of FilterFactory. diff --git a/common_content_filter/src/FilterExpressionParserImpl/identifiers.hpp b/common_content_filter/src/FilterExpressionParserImpl/identifiers.hpp new file mode 100644 index 0000000..fc33d7d --- /dev/null +++ b/common_content_filter/src/FilterExpressionParserImpl/identifiers.hpp @@ -0,0 +1,189 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file identifiers.hpp + * + * Note: this is an implementation file, designed to be included inside the + * FilterExpressionParser.hpp file of the parent folder. + */ + +#ifndef FILTEREXPRESSIONPARSERIMPL__IDENTIFIERS_HPP_ +#define FILTEREXPRESSIONPARSERIMPL__IDENTIFIERS_HPP_ + +#include +#include +#include +#include +struct CurrentIdentifierState +{ + const rosidl_message_type_support_t * type_support; + + const rosidl_message_type_support_t * current_type_support; + + uint8_t current_type; + + std::vector access_path; +}; + +struct identifier_processor + : parse_tree::apply +{ + template + static void + add_access_path( + CurrentIdentifierState & identifier_state, + std::unique_ptr & n, + const rosidl_message_type_support_t * type_support_introspection) + { + const MembersType * members = + static_cast(type_support_introspection->data); + if (!members) { + throw std::runtime_error("The data in the type support introspection is invalid."); + } + + size_t member_index = 0; + size_t array_index = std::numeric_limits::max(); + + const ParseNode & name_node = n->left(); + std::string parse_node_name = name_node.content(); + + for (uint32_t i = 0; i < members->member_count_; ++i) { + const auto member = members->members_ + i; + + if (member->name_ == parse_node_name) { + member_index = i; + bool has_index = n->children.size() > 1; + if (member->is_array_) { + if (!has_index) { + throw parse_error("field should have an index (i.e. [n])", n->left().end()); + } + + array_index = static_cast(std::stoul(n->right().left().content())); + + if (member->array_size_ && !member->is_upper_bound_) { + if (member->array_size_ <= array_index) { + throw parse_error("index is greater than maximum size", n->right().end()); + } + } + } else { + if (has_index) { + throw parse_error("field is not an array or sequence", n->right().begin()); + } + } + + identifier_state.current_type = member->type_id_; + + if (member->type_id_ == ::rosidl_typesupport_introspection_cpp::ROS_TYPE_MESSAGE) { + identifier_state.current_type_support = member->members_; + } + + identifier_state.access_path.emplace_back( + FilterField::FieldAccessor{member_index, array_index, type_support_introspection}); + + return; + } + } + throw parse_error("field not found", name_node.begin()); + } + + static void add_member_access( + std::unique_ptr & n, + CurrentIdentifierState & identifier_state, + const rosidl_message_type_support_t * type_support) + { + const rosidl_message_type_support_t * type_support_introspection = + get_type_support_introspection(type_support); + if (!type_support_introspection) { + throw std::runtime_error("failed to get type support introspection"); + } + + if (type_support_introspection->typesupport_identifier == + rosidl_typesupport_introspection_c__identifier) + { + add_access_path( + identifier_state, n, type_support_introspection); + } else { + add_access_path( + identifier_state, n, type_support_introspection); + } + } + + static FilterValue::ValueKind get_value_kind( + uint8_t type_id, + const position & pos) + { + switch (type_id) { + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_BOOLEAN: + return FilterValue::ValueKind::BOOLEAN; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_CHAR: + return FilterValue::ValueKind::CHAR; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_STRING: + return FilterValue::ValueKind::STRING; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT8: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT16: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT32: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT64: + return FilterValue::ValueKind::SIGNED_INTEGER; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_OCTET: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT8: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT16: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT32: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT64: + return FilterValue::ValueKind::UNSIGNED_INTEGER; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_FLOAT: + return FilterValue::ValueKind::FLOAT_FIELD; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_DOUBLE: + return FilterValue::ValueKind::DOUBLE_FIELD; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_LONG_DOUBLE: + return FilterValue::ValueKind::LONG_DOUBLE_FIELD; + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_MESSAGE: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_WCHAR: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_WSTRING: + break; + } + + throw parse_error("type is not primitive", pos); + } + + template + static void transform( + std::unique_ptr & n, + CurrentIdentifierState & state, + States &&... /*st*/) + { + if (n->is()) { + // Set data for fieldname node + n->field_kind = get_value_kind(state.current_type, n->end()); + n->field_access_path = state.access_path; + n->type_id = state.current_type; + + // Reset parser state + state.access_path.clear(); + state.current_type = 0; + state.current_type_support = nullptr; + } else { + if (0 == state.current_type) { + add_member_access(n, state, state.type_support); + } else { + add_member_access(n, state, state.current_type_support); + } + } + + n->children.clear(); + } +}; + +#endif // FILTEREXPRESSIONPARSERIMPL__IDENTIFIERS_HPP_ diff --git a/common_content_filter/src/FilterExpressionParserImpl/literal_values.hpp b/common_content_filter/src/FilterExpressionParserImpl/literal_values.hpp new file mode 100644 index 0000000..c4d63a0 --- /dev/null +++ b/common_content_filter/src/FilterExpressionParserImpl/literal_values.hpp @@ -0,0 +1,70 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file literal_values.hpp + * + * Note: this is an implementation file, designed to be included inside the + * FilterExpressionParser.hpp file of the parent folder. + */ + +#ifndef FILTEREXPRESSIONPARSERIMPL__LITERAL_VALUES_HPP_ +#define FILTEREXPRESSIONPARSERIMPL__LITERAL_VALUES_HPP_ + +#include +struct literal_value_processor + : parse_tree::apply +{ + template + static void transform( + std::unique_ptr & n, + States &&... /*st*/) + { + n->value.reset(new FilterValue()); + if (n->is()) { + n->value->kind = FilterValue::ValueKind::BOOLEAN; + n->value->boolean_value = true; + } else if (n->is()) { + n->value->kind = FilterValue::ValueKind::BOOLEAN; + n->value->boolean_value = false; + } else if (n->is() || n->is()) { + if (n->m_begin.data[0] == '-') { + n->value->kind = FilterValue::ValueKind::SIGNED_INTEGER; + n->value->signed_integer_value = std::stoll(n->content(), nullptr, 0); + } else { + n->value->kind = FilterValue::ValueKind::UNSIGNED_INTEGER; + n->value->unsigned_integer_value = std::stoull(n->content(), nullptr, 0); + } + } else if (n->is()) { + n->value->kind = FilterValue::ValueKind::FLOAT_CONST; + n->value->float_value = std::stold(n->content()); + } else if (n->is()) { + n->value->kind = FilterValue::ValueKind::CHAR; + n->value->char_value = n->m_begin.data[1]; + } else if (n->is()) { + const ParseNode & content_node = n->left(); + size_t str_len = content_node.m_end.byte - content_node.m_begin.byte; + if (sizeof(n->value->string_value) < str_len) { + throw parse_error("string constant has more than 255 characters", n->end()); + } + n->value->kind = FilterValue::ValueKind::STRING; + strncpy(n->value->string_value, content_node.m_begin.data, str_len); + } + + n->children.clear(); + } +}; + +#endif // FILTEREXPRESSIONPARSERIMPL__LITERAL_VALUES_HPP_ diff --git a/common_content_filter/src/FilterExpressionParserImpl/parameters.hpp b/common_content_filter/src/FilterExpressionParserImpl/parameters.hpp new file mode 100644 index 0000000..cfdf584 --- /dev/null +++ b/common_content_filter/src/FilterExpressionParserImpl/parameters.hpp @@ -0,0 +1,43 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file parameters.hpp + * + * Note: this is an implementation file, designed to be included inside the + * FilterExpressionParser.hpp file of the parent folder. + */ + +#ifndef FILTEREXPRESSIONPARSERIMPL__PARAMETERS_HPP_ +#define FILTEREXPRESSIONPARSERIMPL__PARAMETERS_HPP_ + +#include +struct parameter_processor + : parse_tree::apply +{ + template + static void transform( + std::unique_ptr & n, + States &&... /*st*/) + { + n->parameter_index = static_cast(n->m_begin.data[1] - '0'); + if (n->m_end.byte - n->m_begin.byte == 3) { + n->parameter_index *= 10; + n->parameter_index += static_cast(n->m_begin.data[2] - '0'); + } + } +}; + +#endif // FILTEREXPRESSIONPARSERIMPL__PARAMETERS_HPP_ diff --git a/common_content_filter/src/FilterExpressionParserImpl/rearrange.hpp b/common_content_filter/src/FilterExpressionParserImpl/rearrange.hpp new file mode 100644 index 0000000..686c170 --- /dev/null +++ b/common_content_filter/src/FilterExpressionParserImpl/rearrange.hpp @@ -0,0 +1,76 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file rearrange.hpp + * + * Note: this is an implementation file, designed to be included inside the + * FilterExpressionParser.hpp file of the parent folder. + */ + +#ifndef FILTEREXPRESSIONPARSERIMPL__REARRANGE_HPP_ +#define FILTEREXPRESSIONPARSERIMPL__REARRANGE_HPP_ + +#include +#include +#include + +// after a node is stored successfully, you can add an optional transformer like this: +struct rearrange + : parse_tree::apply // allows bulk selection, see selector<...> +{ + // recursively rearrange nodes. the basic principle is: + // + // from: SEQ_BASED_RULE + // / | \ (LHS... may be one or more children, followed by OP,) + // LHS... OP RHS (which is one operator, and RHS, which is a single child) + // + // to: OP + // / \ (OP now has two children, the original PROD/EXPR and RHS) + // SEQ_BASED_RULE RHS (Note that PROD/EXPR has two fewer children now) + // | + // LHS... + // + // if only one child is left for LHS..., replace the SEQ_BASED_RULE with the child directly. + // otherwise, perform the above transformation, then apply it recursively until LHS... + // becomes a single child, which then replaces the parent node and the recursion ends. + template + static void transform( + std::unique_ptr & n, + States &&... st) + { + if (n->children.size() == 1) { + n = std::move(n->children.back()); + } else { + n->remove_content(); + auto & c = n->children; + auto r = std::move(c.back()); + c.pop_back(); + auto o = std::move(c.back()); + c.pop_back(); + if (c.empty()) { + o->children.emplace_back(std::move(r)); + n = std::move(o); + } else { + o->children.emplace_back(std::move(n)); + o->children.emplace_back(std::move(r)); + n = std::move(o); + transform(n->children.front(), st ...); + } + } + } +}; + +#endif // FILTEREXPRESSIONPARSERIMPL__REARRANGE_HPP_ diff --git a/common_content_filter/src/FilterFactory.cpp b/common_content_filter/src/FilterFactory.cpp new file mode 100644 index 0000000..1fe8c33 --- /dev/null +++ b/common_content_filter/src/FilterFactory.cpp @@ -0,0 +1,470 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterFactory.cpp + */ + +#include "FilterFactory.hpp" + +#include +#include +#include +#include +#include + +#include "IContentFilter.hpp" +#include "IContentFilterFactory.hpp" +#include "FilterCompoundCondition.hpp" +#include "FilterCondition.hpp" +#include "FilterConditionState.hpp" +#include "FilterEmptyExpression.hpp" +#include "FilterExpression.hpp" +#include "FilterExpressionParser.hpp" +#include "FilterField.hpp" +#include "FilterGrammar.hpp" +#include "FilterParseNode.hpp" +#include "FilterParameter.hpp" +#include "FilterPredicate.hpp" +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +static IContentFilterFactory::ReturnCode_t transform_enum( + std::shared_ptr & value, + uint8_t type, + const std::string & string_value) +{ + static_cast(value); + static_cast(type); + static_cast(string_value); + // TODO(iuhilnehc-ynos): when enum supported in the rosidl + return IContentFilterFactory::ReturnCode_t::RETCODE_BAD_PARAMETER; +} + +static IContentFilterFactory::ReturnCode_t transform_enums( + std::shared_ptr & left_value, + uint8_t left_type, + std::shared_ptr & right_value, + uint8_t right_type) +{ + if ((FilterValue::ValueKind::ENUM == left_value->kind) && + (FilterValue::ValueKind::STRING == right_value->kind)) + { + return transform_enum(right_value, left_type, right_value->string_value); + } + + if ((FilterValue::ValueKind::ENUM == right_value->kind) && + (FilterValue::ValueKind::STRING == left_value->kind)) + { + return transform_enum(left_value, right_type, left_value->string_value); + } + + return IContentFilterFactory::ReturnCode_t::RETCODE_OK; +} + +static bool check_value_compatibility( + FilterValue::ValueKind left, + FilterValue::ValueKind right, + bool ignore_enum) +{ + if (!ignore_enum && FilterValue::ValueKind::ENUM == right) { + return FilterValue::ValueKind::ENUM == left || + FilterValue::ValueKind::SIGNED_INTEGER == left || + FilterValue::ValueKind::UNSIGNED_INTEGER == left || + FilterValue::ValueKind::STRING == left; + } + + switch (left) { + case FilterValue::ValueKind::BOOLEAN: + return FilterValue::ValueKind::BOOLEAN == right || + FilterValue::ValueKind::SIGNED_INTEGER == right || + FilterValue::ValueKind::UNSIGNED_INTEGER == right; + + case FilterValue::ValueKind::SIGNED_INTEGER: + case FilterValue::ValueKind::UNSIGNED_INTEGER: + return FilterValue::ValueKind::SIGNED_INTEGER == right || + FilterValue::ValueKind::UNSIGNED_INTEGER == right || + FilterValue::ValueKind::BOOLEAN == right || + FilterValue::ValueKind::FLOAT_CONST == right || + FilterValue::ValueKind::FLOAT_FIELD == right || + FilterValue::ValueKind::DOUBLE_FIELD == right || + FilterValue::ValueKind::LONG_DOUBLE_FIELD == right; + + case FilterValue::ValueKind::CHAR: + case FilterValue::ValueKind::STRING: + return FilterValue::ValueKind::CHAR == right || + FilterValue::ValueKind::STRING == right; + + case FilterValue::ValueKind::FLOAT_CONST: + case FilterValue::ValueKind::FLOAT_FIELD: + case FilterValue::ValueKind::DOUBLE_FIELD: + case FilterValue::ValueKind::LONG_DOUBLE_FIELD: + return FilterValue::ValueKind::FLOAT_CONST == right || + FilterValue::ValueKind::FLOAT_FIELD == right || + FilterValue::ValueKind::DOUBLE_FIELD == right || + FilterValue::ValueKind::LONG_DOUBLE_FIELD == right || + FilterValue::ValueKind::SIGNED_INTEGER == right || + FilterValue::ValueKind::UNSIGNED_INTEGER == right; + + case FilterValue::ValueKind::ENUM: + if (!ignore_enum) { + return FilterValue::ValueKind::ENUM == right || + FilterValue::ValueKind::SIGNED_INTEGER == right || + FilterValue::ValueKind::UNSIGNED_INTEGER == right || + FilterValue::ValueKind::STRING == right; + } + } + + return false; +} + +static FilterPredicate::OperationKind get_predicate_op( + const parser::ParseNode & node) +{ + FilterPredicate::OperationKind ret_val = FilterPredicate::OperationKind::EQUAL; + if (node.is()) { + ret_val = FilterPredicate::OperationKind::EQUAL; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::NOT_EQUAL; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::LESS_THAN; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::LESS_EQUAL; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::GREATER_THAN; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::GREATER_EQUAL; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::LIKE; + } else if (node.is()) { + ret_val = FilterPredicate::OperationKind::MATCH; + } else { + assert(false); + } + + return ret_val; +} + +struct ExpressionParsingState +{ + const void * type_object; + const IContentFilterFactory::ParameterSeq & filter_parameters; + FilterExpression * filter; +}; + +template<> +IContentFilterFactory::ReturnCode_t FilterFactory::convert_tree( + ExpressionParsingState & state, + std::unique_ptr & condition, + const parser::ParseNode & node); + + +template<> +IContentFilterFactory::ReturnCode_t FilterFactory::convert_tree( + ExpressionParsingState & state, + std::shared_ptr & value, + const parser::ParseNode & node) +{ + if (node.value) { + value = std::make_shared(); + value->copy_from(*node.value.get(), true); + } else if (0 != node.type_id) { + std::string field_name = node.content(); + auto it = state.filter->fields.find(field_name); + if (it == state.filter->fields.end()) { + value = state.filter->fields[field_name] = + std::make_shared(node.type_id, node.field_access_path, node.field_kind); + } else { + value = it->second; + } + } else { + // Check parameter index + if (node.parameter_index >= state.filter_parameters.size()) { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + if (state.filter->parameters[node.parameter_index]) { + value = state.filter->parameters[node.parameter_index]; + } else { + auto param_value = std::make_shared(); + if (!param_value->set_value(state.filter_parameters[node.parameter_index].c_str())) { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + value = state.filter->parameters[node.parameter_index] = param_value; + } + } + + return ReturnCode_t::RETCODE_OK; +} + +template<> +IContentFilterFactory::ReturnCode_t FilterFactory::convert_tree( + ExpressionParsingState & state, + std::unique_ptr & condition, + const parser::ParseNode & node) +{ + std::shared_ptr left; + std::shared_ptr right; + ReturnCode_t ret = convert_tree(state, left, node.left()); + if (ReturnCode_t::RETCODE_OK == ret) { + ret = convert_tree(state, right, node.right()); + if (ReturnCode_t::RETCODE_OK == ret) { + bool ignore_enum = false; + if (node.is() || node.is()) { + // At least one fieldname should be a string + if (!((node.left().is() && (FilterValue::ValueKind::STRING == left->kind)) || + (node.right().is() && (FilterValue::ValueKind::STRING == right->kind)))) + { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + ignore_enum = true; + } + + if ((FilterValue::ValueKind::ENUM == left->kind) && + (FilterValue::ValueKind::ENUM == right->kind)) + { + if (node.left().type_id != node.right().type_id) { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + } else if (!check_value_compatibility(left->kind, right->kind, ignore_enum)) { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + ret = transform_enums(left, node.left().type_id, right, node.right().type_id); + if (ReturnCode_t::RETCODE_OK == ret) { + condition.reset(new FilterPredicate(get_predicate_op(node), left, right)); + } + } + } + + return ret; +} + +template<> +IContentFilterFactory::ReturnCode_t FilterFactory::convert_tree( + ExpressionParsingState & state, + std::unique_ptr & condition, + const parser::ParseNode & node) +{ + /* The nodes here will be in the following situation: + * + * between_op + * / \ + * fieldname and_op + * / \ + * op1 op2 + */ + + std::shared_ptr field; + ReturnCode_t ret = convert_tree(state, field, node.left()); + if (ReturnCode_t::RETCODE_OK == ret) { + const parser::ParseNode & and_node = node.right(); + assert(and_node.is()); + + std::shared_ptr op1; + std::shared_ptr op2; + + ret = convert_tree(state, op1, and_node.left()); + if (ReturnCode_t::RETCODE_OK == ret) { + ret = convert_tree(state, op2, and_node.right()); + } + + if (ReturnCode_t::RETCODE_OK == ret) { + if (!check_value_compatibility(field->kind, op1->kind, false) || + !check_value_compatibility(field->kind, op2->kind, false) || + !check_value_compatibility(op1->kind, op2->kind, false)) + { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + ret = transform_enums(field, node.left().type_id, op1, and_node.left().type_id); + if (ReturnCode_t::RETCODE_OK == ret) { + ret = transform_enums(field, node.left().type_id, op2, and_node.right().type_id); + } + } + + if (ReturnCode_t::RETCODE_OK == ret) { + FilterPredicate::OperationKind binary_op = node.is() ? + FilterPredicate::OperationKind::LESS_EQUAL : + FilterPredicate::OperationKind::GREATER_THAN; + FilterCompoundCondition::OperationKind logical_op = node.is() ? + FilterCompoundCondition::OperationKind::AND : + FilterCompoundCondition::OperationKind::OR; + + std::unique_ptr left_cond(new FilterPredicate(binary_op, op1, field)); + std::unique_ptr right_cond(new FilterPredicate(binary_op, field, op2)); + auto cond = + new FilterCompoundCondition( + logical_op, std::move(left_cond), std::move( + right_cond)); + condition.reset(cond); + } + } + + return ret; +} + +template<> +IContentFilterFactory::ReturnCode_t FilterFactory::convert_tree( + ExpressionParsingState & state, + std::unique_ptr & condition, + const parser::ParseNode & node) +{ + ReturnCode_t ret = ReturnCode_t::RETCODE_UNSUPPORTED; + FilterCompoundCondition::OperationKind op = FilterCompoundCondition::OperationKind::NOT; + std::unique_ptr left; + std::unique_ptr right; + + if (node.is()) { + op = FilterCompoundCondition::OperationKind::NOT; + ret = convert_tree(state, left, node.left()); + } else if (node.is()) { + op = FilterCompoundCondition::OperationKind::AND; + ret = convert_tree(state, left, node.left()); + if (ReturnCode_t::RETCODE_OK == ret) { + ret = convert_tree(state, right, node.right()); + } + } else if (node.is()) { + op = FilterCompoundCondition::OperationKind::OR; + ret = convert_tree(state, left, node.left()); + if (ReturnCode_t::RETCODE_OK == ret) { + ret = convert_tree(state, right, node.right()); + } + } else { + assert(false); + } + + if (ReturnCode_t::RETCODE_OK == ret) { + condition.reset(new FilterCompoundCondition(op, std::move(left), std::move(right))); + } + + return ret; +} + +template<> +IContentFilterFactory::ReturnCode_t FilterFactory::convert_tree( + ExpressionParsingState & state, + std::unique_ptr & condition, + const parser::ParseNode & node) +{ + if (node.is() || node.is() || node.is()) { + return convert_tree(state, condition, node); + } else if (node.is() || node.is()) { + return convert_tree(state, condition, node); + } + + return convert_tree(state, condition, node); +} + +FilterFactory::~FilterFactory() +{ + auto & pool = expression_pool_.collection(); + for (FilterExpression * item : pool) { + delete item; + } + pool.clear(); +} + +IContentFilterFactory::ReturnCode_t FilterFactory::create_content_filter( + const rosidl_message_type_support_t * type_support, + const char * filter_expression, + const IContentFilterFactory::ParameterSeq & filter_parameters, + IContentFilter * & filter_instance) +{ + ReturnCode_t ret = ReturnCode_t::RETCODE_UNSUPPORTED; + + if (nullptr == filter_expression) { + if (nullptr == filter_instance) { + ret = ReturnCode_t::RETCODE_BAD_PARAMETER; + } else { + ret = ReturnCode_t::RETCODE_OK; + if (&empty_expression_ != filter_instance) { + auto expr = static_cast(filter_instance); + auto n_params = expr->parameters.size(); + if (filter_parameters.size() < n_params) { + ret = ReturnCode_t::RETCODE_BAD_PARAMETER; + } else { + std::vector old_values(n_params); + size_t n = n_params; + while ((n > 0) && (ReturnCode_t::RETCODE_OK == ret)) { + --n; + if (expr->parameters[n]) { + old_values[n].copy_from(*(expr->parameters[n]), true); + if (!expr->parameters[n]->set_value(filter_parameters[n].c_str())) { + ret = ReturnCode_t::RETCODE_BAD_PARAMETER; + } + } + } + + if (ReturnCode_t::RETCODE_OK != ret) { + while (n < n_params) { + expr->parameters[n]->copy_from(old_values[n], true); + ++n; + } + } + } + } + } + } else if (std::strlen(filter_expression) == 0) { + delete_content_filter(filter_instance); + filter_instance = nullptr; + ret = ReturnCode_t::RETCODE_OK; + } else { + auto node = parser::parse_filter_expression(filter_expression, type_support); + if (node) { + FilterExpression * expr = get_expression(); + size_t n_params = filter_parameters.size(); + expr->parameters.reserve(n_params); + while (expr->parameters.size() < n_params) { + expr->parameters.emplace_back(); + } + ExpressionParsingState state{nullptr, filter_parameters, expr}; + ret = convert_tree(state, expr->root, *(node->children[0])); + if (ReturnCode_t::RETCODE_OK == ret) { + delete_content_filter(filter_instance); + filter_instance = expr; + } else { + delete_content_filter(expr); + } + } else { + ret = ReturnCode_t::RETCODE_BAD_PARAMETER; + } + } + + return ret; +} + +IContentFilterFactory::ReturnCode_t FilterFactory::delete_content_filter( + IContentFilter * filter_instance) +{ + if (nullptr == filter_instance) { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + if (&empty_expression_ != filter_instance) { + auto expr = static_cast(filter_instance); + expr->clear(); + expression_pool_.put(expr); + } + return ReturnCode_t::RETCODE_OK; +} + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterFactory.hpp b/common_content_filter/src/FilterFactory.hpp new file mode 100644 index 0000000..1feebc1 --- /dev/null +++ b/common_content_filter/src/FilterFactory.hpp @@ -0,0 +1,90 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterFactory.hpp + */ + +#ifndef FILTERFACTORY_HPP_ +#define FILTERFACTORY_HPP_ + +#include "IContentFilterFactory.hpp" + +#include "IContentFilter.hpp" + +#include "FilterEmptyExpression.hpp" +#include "FilterExpression.hpp" +#include "ObjectPool.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * An IContentFilterFactory that processes -SQL filter expressions. + */ +class FilterFactory final : public IContentFilterFactory +{ +public: + ~FilterFactory(); + + ReturnCode_t create_content_filter( + const rosidl_message_type_support_t * type_support, + const char * filter_expression, + const ParameterSeq & filter_parameters, + IContentFilter * & filter_instance) override; + + ReturnCode_t delete_content_filter( + IContentFilter * filter_instance) override; + +private: + /** + * Retrieve a FilterExpression from the pool. + * + * @return A pointer to an empty FilterExpression. + */ + FilterExpression * get_expression() + { + return expression_pool_.get( + [] + { + return new FilterExpression(); + }); + } + + /** + * Generic method to perform processing of an AST node resulting from the parsing of a -SQL filter expression. + * Provides a generic mechanism for methods that perform post-processing of the generated AST tree, so they could + * have access to the private fields of FilterFactory. + * + * @return return code indicating the conversion result. + */ + template + ReturnCode_t convert_tree( + _State & state, + _Output & parse_output, + const _ParserNode & node); + + /// Empty expressions content filter + FilterEmptyExpression empty_expression_; + /// Pool of FilterExpression objects + ObjectPool expression_pool_; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERFACTORY_HPP_ diff --git a/common_content_filter/src/FilterField.cpp b/common_content_filter/src/FilterField.cpp new file mode 100644 index 0000000..24f81c0 --- /dev/null +++ b/common_content_filter/src/FilterField.cpp @@ -0,0 +1,202 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterField.cpp + */ + +#include "FilterField.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "FilterPredicate.hpp" +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + + +template +bool +FilterField::get_msg_data_address( + const void * untype_members, + FieldAccessor & accessor, + const void * & data) +{ + const MembersType * members = static_cast(untype_members); + if (!members) { + return false; + } + + const auto member = members->members_ + accessor.member_index; + + uint64_t addr = reinterpret_cast(data); + if (member->is_array_) { + size_t array_size = member->array_size_; + if (array_size == 0) { + array_size = member->size_function( + reinterpret_cast(addr + member->offset_)); + } + + if (accessor.array_index >= array_size) { + return false; + } + + data = member->get_function( + reinterpret_cast(addr + member->offset_), accessor.array_index); + } else { + data = reinterpret_cast(addr + member->offset_); + } + + return true; +} + +bool FilterField::set_value( + const void * data, + size_t n) +{ + bool last_step = access_path_.size() - 1 == n; + bool ret = false; + bool is_c_type_support; + + const rosidl_message_type_support_t * type_support_introspection = + access_path_[n].type_support_introspection; + if (type_support_introspection->typesupport_identifier == + rosidl_typesupport_introspection_c__identifier) + { + is_c_type_support = true; + ret = get_msg_data_address( + type_support_introspection->data, access_path_[n], data); + + } else { + is_c_type_support = false; + ret = get_msg_data_address( + type_support_introspection->data, access_path_[n], data); + } + + if (ret) { + if (last_step) { + ret = set_member(data, is_c_type_support); + + has_value_ = true; + value_has_changed(); + + // Inform parent predicates + for (FilterPredicate * parent : parents_) { + parent->value_has_changed(); + } + } else { + ret = set_value(data, n + 1); + } + } + + return ret; +} + +bool FilterField::set_member( + const void * data, + bool is_c_type_support) +{ + bool ret = true; + + switch (type_id_) { + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_FLOAT: + float_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_DOUBLE: + float_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_LONG_DOUBLE: + float_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_CHAR: + char_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_BOOLEAN: + boolean_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_OCTET: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT8: + unsigned_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT8: + signed_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT16: + unsigned_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT16: + signed_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT32: + unsigned_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT32: + signed_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_UINT64: + unsigned_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_INT64: + signed_integer_value = *reinterpret_cast(data); + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_STRING: + + if (is_c_type_support) { + auto c_string = reinterpret_cast(data); + strncpy(string_value, c_string->data, sizeof(string_value)); + } else { + auto string = reinterpret_cast(data); + strncpy(string_value, string->c_str(), sizeof(string_value)); + } + break; + + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_WCHAR: + case ::rosidl_typesupport_introspection_cpp::ROS_TYPE_WSTRING: + default: + ret = false; + break; + } + + return ret; +} + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterField.hpp b/common_content_filter/src/FilterField.hpp new file mode 100644 index 0000000..54a9418 --- /dev/null +++ b/common_content_filter/src/FilterField.hpp @@ -0,0 +1,155 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterField.hpp + */ + +#ifndef FILTERFIELD_HPP_ +#define FILTERFIELD_HPP_ + +#include + +#include +#include +#include + +#include "FilterPredicate.hpp" +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * A FilterValue for fieldname-based expression values. + */ +class FilterField final : public FilterValue +{ +public: + /** + * An element on the access path to the final field. + */ + struct FieldAccessor final + { + /// Index of the member to access + size_t member_index; + + /// Element index for array / sequence members + size_t array_index; + + /// Type support introspection information for current field + const rosidl_message_type_support_t * type_support_introspection; + }; + + /** + * Construct a FilterField. + * + * @param[in] type_id TypeIdentifier representing the primitive data type of the fieldname. + * @param[in] access_path Access path to the field. + * @param[in] data_kind Kind of data the field represents. + */ + FilterField( + uint8_t type_id, + const std::vector & access_path, + ValueKind data_kind) + : FilterValue(data_kind) + , access_path_(access_path) + , type_id_(type_id) + { + } + + virtual ~FilterField() = default; + + /** + * This method is used by a FilterPredicate to check if this FilterField can be used. + * + * @return whether this FilterField has a value that can be used on a predicate. + */ + inline bool has_value() const noexcept final + { + return has_value_; + } + + /** + * Instruct this value to reset. + */ + inline void reset() noexcept final + { + has_value_ = false; + } + + + /** + * Perform the deserialization of the field represented by this FilterField. + * Will notify the predicates where this FilterField is being used. + * + * @param[in] data The dynamic representation of the payload being filtered. + * + * @return Whether the deserialization process succeeded. + * + * @post Method @c has_value returns true. + */ + inline bool set_value( + const void * data_value) + { + return set_value(data_value, static_cast(0)); + } + + /** + * Perform the deserialization of a specific step of the access path. + * + * @param[in] data The dynamic representation of the step being processed. + * @param[in] n The step on the access path being processed. + * + * @return Whether the deserialization process succeeded. + * + * @post Method @c has_value returns true. + */ + bool set_value( + const void * data_value, + size_t n); + +protected: + inline void add_parent( + FilterPredicate * parent) final + { + assert(nullptr != parent); + parents_.emplace(parent); + } + +private: + template + bool + get_msg_data_address( + const void * untype_members, + FieldAccessor & accessor, + const void * & data); + + bool set_member( + const void * data_value, + bool is_c_type_support); + + bool has_value_ = false; + std::vector access_path_; + uint8_t type_id_ = 0; + std::unordered_set parents_; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERFIELD_HPP_ diff --git a/common_content_filter/src/FilterGrammar.hpp b/common_content_filter/src/FilterGrammar.hpp new file mode 100644 index 0000000..be089d3 --- /dev/null +++ b/common_content_filter/src/FilterGrammar.hpp @@ -0,0 +1,126 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterGrammar.hpp + */ + +#ifndef FILTERGRAMMAR_HPP_ +#define FILTERGRAMMAR_HPP_ + +#include +#include +#include + +namespace common_content_filter +{ +namespace SQLFilter +{ + +using namespace tao::TAO_PEGTL_NAMESPACE; // NOLINT + +// *INDENT-OFF* Allow curly braces on the same line to improve grammar readability + +// Some basic constructs +struct sign : one< '+', '-'> {}; +struct integer : plus< digit > {}; + +// FIELDNAME +struct dot_op : one< '.' > {}; +struct index_part : seq< one<'['>, integer, one<']'> > {}; +struct fieldname_part : seq< identifier, opt< index_part > > {}; +struct fieldname : list {}; + +// CHARVALUE, STRING, ENUMERATEDVALUE +struct open_quote : one< '\'' > {}; +struct close_quote : one< '\'' > {}; +struct char_value : seq< open_quote, any, close_quote > {}; +struct string_content : star< not_one< '\'', '\r', '\n'> > {}; +struct string_value : seq< open_quote, string_content, close_quote > {}; + +// BOOLEANVALUE +struct false_value : pad< sor< TAO_PEGTL_KEYWORD("FALSE"), TAO_PEGTL_KEYWORD("false") >, space > {}; +struct true_value : pad< sor< TAO_PEGTL_KEYWORD("TRUE"), TAO_PEGTL_KEYWORD("true") >, space > {}; +struct boolean_value : sor {}; + +// INTEGERVALUE, FLOATVALUE +struct integer_value : seq< opt< sign >, integer > {}; +struct fractional : seq< dot_op, integer > {}; +struct exponent : seq< one< 'e', 'E' >, integer_value > {}; +struct float_value : + seq < opt< sign >, integer, sor < exponent, seq< fractional, opt< exponent > > > > {}; +struct hex_value : seq< opt< sign >, one< '0' >, one< 'x', 'X' >, plus< xdigit > > {}; + +// PARAMETER +struct parameter_value : seq< one< '%' >, digit, opt< digit > > {}; + +// Keyword based operators +struct and_op : pad< sor< TAO_PEGTL_KEYWORD("AND"), TAO_PEGTL_KEYWORD("and") >, space > {}; +struct or_op : pad< sor< TAO_PEGTL_KEYWORD("OR"), TAO_PEGTL_KEYWORD("or") >, space> {}; +struct not_op : pad< sor< TAO_PEGTL_KEYWORD("NOT"), TAO_PEGTL_KEYWORD("not") >, space> {}; +struct between_op : + pad< sor< TAO_PEGTL_KEYWORD("BETWEEN"), TAO_PEGTL_KEYWORD("between") >, space> {}; +struct not_between_op : + pad< sor< TAO_PEGTL_KEYWORD("NOT BETWEEN"), TAO_PEGTL_KEYWORD("not between") >, space> {}; + +// RelOp +struct eq_op : pad< one<'='>, space> {}; +struct gt_op : pad< one<'>'>, space> {}; +struct ge_op : pad< TAO_PEGTL_KEYWORD(">="), space> {}; +struct lt_op : pad< one<'<'>, space> {}; +struct le_op : pad< TAO_PEGTL_KEYWORD("<="), space> {}; +struct ne_op : pad< sor< TAO_PEGTL_KEYWORD("<>"), TAO_PEGTL_KEYWORD("!=") >, space> {}; +struct like_op : pad< sor< TAO_PEGTL_KEYWORD("LIKE"), TAO_PEGTL_KEYWORD("like") >, space> {}; +struct match_op : pad< sor< TAO_PEGTL_KEYWORD("MATCH"), TAO_PEGTL_KEYWORD("match") >, space> {}; +struct rel_op : sor< match_op, like_op, ne_op, le_op, ge_op, lt_op, gt_op, eq_op > {}; + +// Parameter, Range +struct Literal : + sor< boolean_value, float_value, hex_value, integer_value, char_value, string_value > {}; +struct Parameter : sor< Literal, parameter_value > {}; +struct Range : seq< Parameter, and_op, Parameter > {}; + +// Predicates +struct BetweenPredicate : seq< fieldname, sor< not_between_op, between_op >, Range > {}; +struct ComparisonPredicate : sor< + seq< Parameter, rel_op, fieldname >, + seq< fieldname, rel_op, Parameter >, + seq< fieldname, rel_op, fieldname > + > {}; +struct Predicate : sor< ComparisonPredicate, BetweenPredicate > {}; + +// Brackets +struct open_bracket : seq< one< '(' >, star< space > > {}; +struct close_bracket : seq< star< space >, one< ')' > > {}; + +// Condition, ConditionList +struct Condition; +struct ConditionList : list_must< Condition, sor< and_op, or_op > > {}; +struct Condition : sor< + seq< open_bracket, ConditionList, close_bracket >, + seq< not_op, ConditionList >, + Predicate + > {}; + +// Main grammar +struct FilterExpressionGrammar : must< ConditionList, tao::TAO_PEGTL_NAMESPACE::eof > {}; +struct LiteralGrammar : must< Literal, tao::TAO_PEGTL_NAMESPACE::eof > {}; + +// *INDENT-ON* + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERGRAMMAR_HPP_ diff --git a/common_content_filter/src/FilterParameter.cpp b/common_content_filter/src/FilterParameter.cpp new file mode 100644 index 0000000..58d6f05 --- /dev/null +++ b/common_content_filter/src/FilterParameter.cpp @@ -0,0 +1,48 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterParameter.cpp + */ + +#include "FilterParameter.hpp" + +#include "FilterExpressionParser.hpp" +#include "Log.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +bool FilterParameter::set_value( + const char * parameter) +{ + auto node = parser::parse_literal_value(parameter); + + if (!node) { + logError(SQLFILTER, "PARSE ERROR: parser::parse_literal_value"); + + return false; + } + + copy_from(*node->left().value, false); + value_has_changed(); + + return true; +} + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterParameter.hpp b/common_content_filter/src/FilterParameter.hpp new file mode 100644 index 0000000..3380b16 --- /dev/null +++ b/common_content_filter/src/FilterParameter.hpp @@ -0,0 +1,52 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterParameter.hpp + */ + +#ifndef FILTERPARAMETER_HPP_ +#define FILTERPARAMETER_HPP_ + +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * A FilterValue for expression parameters (i.e. %nn). + */ +class FilterParameter final : public FilterValue +{ +public: + virtual ~FilterParameter() = default; + + /** + * Sets the value of this FilterParameter given from an input string. + * + * @param[in] parameter The string from which to set the value. + * + * @return whether the parsing of the string correspond to a valid literal value. + */ + bool set_value( + const char * parameter); +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERPARAMETER_HPP_ diff --git a/common_content_filter/src/FilterParseNode.hpp b/common_content_filter/src/FilterParseNode.hpp new file mode 100644 index 0000000..428ab87 --- /dev/null +++ b/common_content_filter/src/FilterParseNode.hpp @@ -0,0 +1,70 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterParseNode.hpp + */ + +#ifndef FILTERPARSENODE_HPP_ +#define FILTERPARSENODE_HPP_ + +#include +#include + +#include + +#include "FilterField.hpp" +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ +namespace parser +{ + +using namespace tao::TAO_PEGTL_NAMESPACE; // NOLINT + +struct ParseNode : parse_tree::basic_node +{ + // When the node is a literal value, it will hold a pointer to it + std::unique_ptr value; + + // When the node is a fieldname, it will hold the access path to the field, the data kind, + // and the type id + std::vector field_access_path; + FilterValue::ValueKind field_kind = FilterValue::ValueKind::STRING; + // ros2 primitive type id + uint8_t type_id = 0; + + // When the node is a parameter, it will hold the parameter index + size_t parameter_index = 0; + + const ParseNode & left() const + { + return *(children[0]); + } + + const ParseNode & right() const + { + return *(children[1]); + } +}; + +} // namespace parser +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERPARSENODE_HPP_ diff --git a/common_content_filter/src/FilterPredicate.cpp b/common_content_filter/src/FilterPredicate.cpp new file mode 100644 index 0000000..f99fba6 --- /dev/null +++ b/common_content_filter/src/FilterPredicate.cpp @@ -0,0 +1,99 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterPredicate.cpp + */ + +#include "FilterPredicate.hpp" + +#include +#include + +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +FilterPredicate::FilterPredicate( + OperationKind op, + const std::shared_ptr & left, + const std::shared_ptr & right) +: op_(op) + , left_(left) + , right_(right) +{ + assert(left_); + assert(right_); + + left_->add_parent(this); + right_->add_parent(this); + + if (OperationKind::LIKE == op_) { + right_->as_regular_expression(true); + } else if (OperationKind::MATCH == op_) { + right_->as_regular_expression(false); + } +} + +void FilterPredicate::value_has_changed() +{ + if (left_->has_value() && right_->has_value()) { + switch (op_) { + case OperationKind::EQUAL: + set_result(*left_ == *right_); + break; + + case OperationKind::NOT_EQUAL: + set_result(*left_ != *right_); + break; + + case OperationKind::LESS_THAN: + set_result(*left_ < *right_); + break; + + case OperationKind::LESS_EQUAL: + set_result(*left_ <= *right_); + break; + + case OperationKind::GREATER_THAN: + set_result(*left_ > *right_); + break; + + case OperationKind::GREATER_EQUAL: + set_result(*left_ >= *right_); + break; + + case OperationKind::LIKE: + case OperationKind::MATCH: + set_result(left_->is_like(*right_)); + break; + + default: + assert(false); + } + } +} + +void FilterPredicate::propagate_reset() noexcept +{ + left_->reset(); + right_->reset(); +} + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterPredicate.hpp b/common_content_filter/src/FilterPredicate.hpp new file mode 100644 index 0000000..ea1e53b --- /dev/null +++ b/common_content_filter/src/FilterPredicate.hpp @@ -0,0 +1,91 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterPredicate.hpp + */ + +#ifndef FILTERPREDICATE_HPP_ +#define FILTERPREDICATE_HPP_ + +#include + +#include "FilterCondition.hpp" +#include "FilterValue.hpp" + +namespace common_content_filter +{ +namespace SQLFilter +{ + +/** + * A FilterCondition for binary predicates (i.e. ). + */ +class FilterPredicate final : public FilterCondition +{ +public: + /** + * Possible kinds of binary operations + */ + enum class OperationKind : uint8_t + { + EQUAL, ///< left = right + NOT_EQUAL, ///< left <> right + LESS_THAN, ///< left < right + LESS_EQUAL, ///< left <= right + GREATER_THAN, ///< left > right + GREATER_EQUAL, ///< left >= right + LIKE, ///< left LIKE right + MATCH ///< left MATCH right + }; + + /** + * Construct a FilterPredicate. + * + * @param[in] op Operation to perform. + * @param[in] left Left operand. + * @param[in] right Right operand. + */ + FilterPredicate( + OperationKind op, + const std::shared_ptr & left, + const std::shared_ptr & right); + + virtual ~FilterPredicate() = default; + + /** + * Called when the value of an operand is changed. + */ + void value_has_changed(); + +protected: + void propagate_reset() noexcept final; + + void child_has_changed( + const FilterCondition & child) noexcept final + { + static_cast(child); + } + +private: + OperationKind op_; + std::shared_ptr left_; + std::shared_ptr right_; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERPREDICATE_HPP_ diff --git a/common_content_filter/src/FilterValue.cpp b/common_content_filter/src/FilterValue.cpp new file mode 100644 index 0000000..e9c0827 --- /dev/null +++ b/common_content_filter/src/FilterValue.cpp @@ -0,0 +1,356 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterValue.cpp + */ + +#include "FilterValue.hpp" + +#include +#include +#include +#include + +namespace common_content_filter +{ +namespace SQLFilter +{ + +template +int compare_values( + T lvalue, + T rvalue) +{ + return lvalue < rvalue ? -1 : + lvalue > rvalue ? 1 : 0; +} + +/** + * Check if a value is negative. + * Only used during promotion to UNSIGNED_INTEGER. + */ +static bool is_negative( + const FilterValue & value) +{ + switch (value.kind) { + case FilterValue::ValueKind::BOOLEAN: + return false; + + case FilterValue::ValueKind::ENUM: + case FilterValue::ValueKind::SIGNED_INTEGER: + return value.signed_integer_value < 0; + + // The rest of the types shall never be promoted to UNSIGNED_INTEGER + default: + assert(false); + } + + return false; +} + +/** + * Performs promotion to SIGNED_INTEGER + */ +static int64_t to_signed_integer( + const FilterValue & value) +{ + switch (value.kind) { + case FilterValue::ValueKind::BOOLEAN: + return value.boolean_value ? 1 : 0; + + case FilterValue::ValueKind::ENUM: + return value.signed_integer_value; + + // The rest of the types shall never be promoted to SIGNED_INTEGER + default: + assert(false); + } + + return 0; +} + +/** + * Performs promotion to UNSIGNED_INTEGER + */ +static uint64_t to_unsigned_integer( + const FilterValue & value) +{ + switch (value.kind) { + case FilterValue::ValueKind::BOOLEAN: + return value.boolean_value ? 1 : 0; + + case FilterValue::ValueKind::ENUM: + case FilterValue::ValueKind::SIGNED_INTEGER: + return static_cast(value.signed_integer_value); + + // The rest of the types shall never be promoted to UNSIGNED_INTEGER + default: + assert(false); + } + + return 0; +} + +/** + * Performs promotion to FLOAT + */ +static long double to_float( + const FilterValue & value) +{ + switch (value.kind) { + case FilterValue::ValueKind::ENUM: + case FilterValue::ValueKind::SIGNED_INTEGER: + return static_cast(value.signed_integer_value); + + case FilterValue::ValueKind::UNSIGNED_INTEGER: + return static_cast(value.unsigned_integer_value); + + case FilterValue::ValueKind::FLOAT_CONST: + case FilterValue::ValueKind::FLOAT_FIELD: + case FilterValue::ValueKind::DOUBLE_FIELD: + case FilterValue::ValueKind::LONG_DOUBLE_FIELD: + return value.float_value; + + // The rest of the types shall never be promoted to FLOAT + default: + assert(false); + } + + return 0; +} + +/** + * Performs promotion to STRING + */ +static void to_string_value( + const FilterValue & in, + std::string & out) +{ + assert(FilterValue::ValueKind::CHAR == in.kind); + out.assign(&in.char_value, 1); +} + +void FilterValue::copy_from( + const FilterValue & other, + bool copy_regular_expression) noexcept +{ + kind = other.kind; + switch (kind) { + case ValueKind::BOOLEAN: + boolean_value = other.boolean_value; + break; + + case ValueKind::CHAR: + char_value = other.char_value; + break; + + case ValueKind::ENUM: + case ValueKind::SIGNED_INTEGER: + signed_integer_value = other.signed_integer_value; + break; + + case ValueKind::UNSIGNED_INTEGER: + unsigned_integer_value = other.unsigned_integer_value; + break; + + case ValueKind::FLOAT_CONST: + case ValueKind::FLOAT_FIELD: + case ValueKind::DOUBLE_FIELD: + case ValueKind::LONG_DOUBLE_FIELD: + float_value = other.float_value; + break; + + case ValueKind::STRING: + strncpy(string_value, other.string_value, sizeof(string_value)); + break; + + default: + assert(false); + } + + if (copy_regular_expression) { + regular_expr_kind_ = other.regular_expr_kind_; + if (has_value()) { + value_has_changed(); + } + } +} + +int FilterValue::compare( + const FilterValue & lhs, + const FilterValue & rhs) noexcept +{ + if (lhs.kind == rhs.kind) { + switch (lhs.kind) { + case ValueKind::BOOLEAN: + return lhs.boolean_value - rhs.boolean_value; + + case ValueKind::CHAR: + return lhs.char_value - rhs.char_value; + + case ValueKind::SIGNED_INTEGER: + case ValueKind::ENUM: + return compare_values(lhs.signed_integer_value, rhs.signed_integer_value); + + case ValueKind::UNSIGNED_INTEGER: + return compare_values(lhs.unsigned_integer_value, rhs.unsigned_integer_value); + + case ValueKind::FLOAT_FIELD: + return compare_values( + static_cast(lhs.float_value), + static_cast(rhs.float_value)); + + case ValueKind::DOUBLE_FIELD: + return compare_values( + static_cast(lhs.float_value), + static_cast(rhs.float_value)); + + case ValueKind::LONG_DOUBLE_FIELD: + return compare_values(lhs.float_value, rhs.float_value); + + case ValueKind::STRING: + return std::strcmp(lhs.string_value, rhs.string_value); + + // As the grammar does not allow constant vs constant comparisons, FLOAT_CONST should never + // reach here + case ValueKind::FLOAT_CONST: + default: + assert(false); + } + } else if (lhs.kind < rhs.kind) { + // We want to always promote rhs + return -compare(rhs, lhs); + } else { + /* Type promotion rules are enforced during the construction of the expression tree on + * FilterFactory. For the types on which promotion is supported, a corresponding to_xxx method exists + * that will assert if an invalid promotion is performed. + */ + switch (lhs.kind) { + case ValueKind::ENUM: + case ValueKind::SIGNED_INTEGER: + { + return compare_values(lhs.signed_integer_value, to_signed_integer(rhs)); + } + + case ValueKind::UNSIGNED_INTEGER: + return is_negative(rhs) ? 1 : compare_values( + lhs.unsigned_integer_value, to_unsigned_integer( + rhs)); + + case ValueKind::FLOAT_CONST: + case ValueKind::FLOAT_FIELD: + return compare_values( + static_cast(lhs.float_value), + static_cast(to_float(rhs))); + + case ValueKind::DOUBLE_FIELD: + return compare_values( + static_cast(lhs.float_value), + static_cast(to_float(rhs))); + + case ValueKind::LONG_DOUBLE_FIELD: + return compare_values(lhs.float_value, to_float(rhs)); + + case ValueKind::STRING: + { + std::string rvalue; + to_string_value(rhs, rvalue); + return std::strcmp(lhs.string_value, rvalue.c_str()); + } + + // Promotion to boolean or char are not allowed. + case ValueKind::BOOLEAN: + case ValueKind::CHAR: + default: + assert(false); + break; + } + } + + return 0; +} + +void FilterValue::as_regular_expression( + bool is_like_operand) +{ + regular_expr_kind_ = is_like_operand ? RegExpKind::LIKE : RegExpKind::MATCH; + if (has_value()) { + value_has_changed(); + } +} + +void FilterValue::value_has_changed() +{ + if (RegExpKind::NONE != regular_expr_kind_) { + std::string expr; + + switch (kind) { + case ValueKind::CHAR: + expr = char_value; + break; + + case ValueKind::STRING: + expr = string_value; + break; + + default: + assert(false); + } + + if (RegExpKind::LIKE == regular_expr_kind_) { + expr = std::regex_replace(expr, std::regex("\\*"), ".*"); + expr = std::regex_replace(expr, std::regex("\\?"), "."); + expr = std::regex_replace(expr, std::regex("%"), ".*"); + expr = std::regex_replace(expr, std::regex("_"), "."); + } + + regular_expr_.reset(new std::regex(expr)); + } +} + +bool FilterValue::is_like( + const FilterValue & other) const noexcept +{ + assert(other.regular_expr_); + + std::string char_string_value; + + switch (kind) { + case ValueKind::CHAR: + assert(ValueKind::STRING == other.kind); + to_string_value(*this, char_string_value); + return std::regex_match(char_string_value.c_str(), *other.regular_expr_); + + case ValueKind::STRING: + switch (other.kind) { + case ValueKind::CHAR: + case ValueKind::STRING: + return std::regex_match(string_value, *other.regular_expr_); + + default: + assert(false); + } + break; + + default: + assert(false); + } + + return false; +} + +} // namespace SQLFilter +} // namespace common_content_filter diff --git a/common_content_filter/src/FilterValue.hpp b/common_content_filter/src/FilterValue.hpp new file mode 100644 index 0000000..3765bc3 --- /dev/null +++ b/common_content_filter/src/FilterValue.hpp @@ -0,0 +1,224 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file FilterValue.hpp + */ + +#ifndef FILTERVALUE_HPP_ +#define FILTERVALUE_HPP_ + +#include +#include + +namespace common_content_filter +{ +namespace SQLFilter +{ + +class FilterPredicate; + +/** + * Represents a value (either constant, parameter or fieldname) on a filter expression. + */ +class FilterValue +{ +public: + // FilterPredicate needs to call protected method add_parent + friend class FilterPredicate; + + /** + * The high-level kind of a FilterValue. + * Please note that the constants here should follow the promotion order. + */ + enum class ValueKind + { + BOOLEAN, ///< Value is a bool + ENUM, ///< Value is an int32_t with the value of an enumeration + SIGNED_INTEGER, ///< Value is a int16_t, int32_t, or int64_t + UNSIGNED_INTEGER, ///< Value is a uint8_t, uint16_t, uint32_t, or uint64_t + FLOAT_CONST, ///< Value is a long double constant + FLOAT_FIELD, ///< Value is a float field + DOUBLE_FIELD, ///< Value is a double field + LONG_DOUBLE_FIELD, ///< Value is a long double field + CHAR, ///< Value is a char + STRING ///< Value is a string + }; + + /// The kind of value held by this FilterValue + ValueKind kind; + + union { + bool boolean_value; ///< Value when kind == BOOL + char char_value; ///< Value when kind == CHAR + int64_t signed_integer_value; ///< Value when kind == SIGNED_INTEGER / ENUM + uint64_t unsigned_integer_value; ///< Value when kind == UNSIGNED_INTEGER + long double float_value; ///< Value when kind == FLOAT + char string_value[255]; ///< Value when kind == STRING + }; + + /** + * Default constructor. + * Constructs an empty string FilterValue + */ + FilterValue() noexcept + : kind(ValueKind::STRING) + , string_value() + { + } + + /** + * Explicit kind constructor. + * Constructs a zero-valued, specific kind FilterValue. + * + * @param[in] kind The kind with which to construct the FilterValue. + */ + explicit FilterValue( + ValueKind data_kind) noexcept + : kind(data_kind) + , string_value() + { + } + + // *INDENT-OFF* + FilterValue(const FilterValue&) = delete; + FilterValue& operator=(const FilterValue&) = delete; + FilterValue(FilterValue&&) = default; + FilterValue& operator=(FilterValue&&) = default; + // *INDENT-ON* + + virtual ~FilterValue() = default; + + /** + * Copy the state of this object from another one. + * + * @param [in] other The FilterValue from where to copy the state. + * @param [in] copy_regular_expression Whether the regular expression state should be copied or not + */ + void copy_from( + const FilterValue & other, + bool copy_regular_expression) noexcept; + + /** + * This method is used by a FilterPredicate to check if this FilterValue can be used. + * Constants and parameters will always have a value, but fieldname-based values can only be + * used after deserialization. + * + * @return whether this FilterValue has a value that can be used on a predicate. + */ + virtual bool has_value() const noexcept + { + return true; + } + + /** + * Instruct this value to reset. + * Will only have effect on fieldname-based values. + */ + virtual void reset() noexcept + { + } + + /** + * Mark that this value should be handled as a regular expression. + * + * @param [in] is_like_operand Whether this value is used on a LIKE or MATCH operation. + */ + void as_regular_expression( + bool is_like_operand); + + /** + * @name Comparison operations + * Methods implementing the comparison operators of binary predicates. + * Should only be called against a FilterValue of a compatible kind, + * according to the type promotion restrictions. + */ + ///@{ + inline bool operator==( + const FilterValue & other) const noexcept + { + return compare(*this, other) == 0; + } + + inline bool operator!=( + const FilterValue & other) const noexcept + { + return compare(*this, other) != 0; + } + + inline bool operator<( + const FilterValue & other) const noexcept + { + return compare(*this, other) < 0; + } + + inline bool operator<=( + const FilterValue & other) const noexcept + { + return compare(*this, other) <= 0; + } + + inline bool operator>( + const FilterValue & other) const noexcept + { + return compare(*this, other) > 0; + } + + inline bool operator>=( + const FilterValue & other) const noexcept + { + return compare(*this, other) >= 0; + } + + bool is_like( + const FilterValue & other) const noexcept; + ///@} + +protected: + /** + * Called when this FilterValue is used on a FilterPredicate. + * + * @param parent [in] The FilterPredicate referencing this FilterValue. + */ + virtual void add_parent( + FilterPredicate * parent) + { + static_cast(parent); + } + + /** + * Called when the value of this FilterValue has changed. + * Will regenerate the regular expression object if as_regular_expression was called. + */ + void value_has_changed(); + +private: + enum class RegExpKind + { + NONE, LIKE, MATCH + }; + + RegExpKind regular_expr_kind_ = RegExpKind::NONE; + std::unique_ptr regular_expr_; + + static int compare( + const FilterValue & lhs, + const FilterValue & rhs) noexcept; +}; + +} // namespace SQLFilter +} // namespace common_content_filter + +#endif // FILTERVALUE_HPP_ diff --git a/common_content_filter/src/IContentFilter.hpp b/common_content_filter/src/IContentFilter.hpp new file mode 100644 index 0000000..641729d --- /dev/null +++ b/common_content_filter/src/IContentFilter.hpp @@ -0,0 +1,45 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file IContentFilter.hpp + */ + +#ifndef ICONTENTFILTER_HPP_ +#define ICONTENTFILTER_HPP_ + + +namespace common_content_filter +{ + +/** + * The interface that content filter objects should implement. + */ +struct IContentFilter +{ + /** + * Evaluate if a de-serialized payload should be accepted by certain reader. + * + * @param [in] payload The de-serialized payload of the sample being evaluated. + * + * @return whether the sample should be accepted. + */ + virtual bool evaluate( + const void * payload) const = 0; +}; + +} // namespace common_content_filter + +#endif // ICONTENTFILTER_HPP_ diff --git a/common_content_filter/src/IContentFilterFactory.hpp b/common_content_filter/src/IContentFilterFactory.hpp new file mode 100644 index 0000000..ae3ab88 --- /dev/null +++ b/common_content_filter/src/IContentFilterFactory.hpp @@ -0,0 +1,91 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file IContentFilter.hpp + */ + +#ifndef ICONTENTFILTERFACTORY_HPP_ +#define ICONTENTFILTERFACTORY_HPP_ + +#include + +#include +#include + +#include "IContentFilter.hpp" + + +namespace common_content_filter +{ + +/** + * The interface that a factory of IContentFilter objects should implement. + */ +struct IContentFilterFactory +{ + enum ReturnCode_t + { + RETCODE_OK, + RETCODE_BAD_PARAMETER, + RETCODE_UNSUPPORTED, + }; + + using ParameterSeq = std::vector; + + /** + * Create or update an IContentFilter instance. + * + * @param [in] type_support Type support of the topic data being filtered. + * @param [in] filter_expression Content filter expression. + * May be nullptr when updating the parameters of a filter instance. + * @param [in] filter_parameters Values to set for the filter parameters (%n on the filter expression). + * @param [in, out] filter_instance When a filter is being created, it will be nullptr on input, + * and will have the pointer to the created filter instance on output. + * The caller takes ownership of the filter instance returned. + * When a filter is being updated, it will have a previously returned pointer + * on input. The method takes ownership of the filter instance during its + * execution, and can update the filter instance or even destroy it and + * create a new one. + * The caller takes ownership of the filter instance returned. + * It should always have a valid pointer upon return. + * The original state of the filter instance should be preserved when an + * error is returned. + * + * @return A return code indicating the result of the operation. + */ + virtual ReturnCode_t create_content_filter( + const rosidl_message_type_support_t * type_support, + const char * filter_expression, + const ParameterSeq & filter_parameters, + IContentFilter * & filter_instance) = 0; + + /** + * Delete an IContentFilter instance. + * + * @param [in] filter_instance A pointer to a filter instance previously returned by create_content_filter. + * The factory takes ownership of the filter instance, and can decide to destroy + * it or keep it for future use. + * In case of deletion, note this pointer must be downcasted to the derived class. + * + * @return A return code indicating the result of the operation. + */ + virtual ReturnCode_t delete_content_filter( + IContentFilter * filter_instance) = 0; +}; + +} // namespace common_content_filter + +#endif // ICONTENTFILTERFACTORY_HPP_ diff --git a/common_content_filter/src/Log.hpp b/common_content_filter/src/Log.hpp new file mode 100644 index 0000000..1599296 --- /dev/null +++ b/common_content_filter/src/Log.hpp @@ -0,0 +1,33 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +#ifndef LOG_HPP_ +#define LOG_HPP_ + +#include +#include + +#define common_content_filter_log_(level, cat, msg) \ + { \ + std::stringstream log_ss_tmp__; \ + log_ss_tmp__ << msg; \ + RCUTILS_LOG_ ## level ## _NAMED(#cat, "%s", log_ss_tmp__.str().c_str()); \ + } + +#define logDebug(cat, msg) common_content_filter_log_(DEBUG, cat, msg) +#define logInfo(cat, msg) common_content_filter_log_(INFO, cat, msg) +#define logWarning(cat, msg) common_content_filter_log_(WARN, cat, msg) +#define logError(cat, msg) common_content_filter_log_(ERROR, cat, msg) + +#endif // LOG_HPP_ diff --git a/common_content_filter/src/ObjectPool.hpp b/common_content_filter/src/ObjectPool.hpp new file mode 100644 index 0000000..500fa0c --- /dev/null +++ b/common_content_filter/src/ObjectPool.hpp @@ -0,0 +1,98 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +/** + * @file ObjectPool.hpp + * + */ + +#ifndef OBJECTPOOL_HPP_ +#define OBJECTPOOL_HPP_ + +#include +#include +#include +#include + +namespace common_content_filter +{ + +/** + * A generic pool of objects. + * + * This template class holds a circular queue of fixed size. Pushing a new element to a full queue + * will result in an error. + * + * @tparam _Ty Element type. + * @tparam _Alloc Allocator to use on the underlying collection type, defaults to std::allocator<_Ty>. + * @tparam _Collection Underlying collection type, defaults to std::vector<_Ty, _Alloc> + * + * @ingroup UTILITIES_MODULE + */ +template< + typename _Ty, + typename _Alloc = std::allocator<_Ty>, + typename _Collection = std::vector<_Ty, _Alloc>> +struct ObjectPool final +{ + using allocator_type = _Alloc; + using value_type = _Ty; + + /** + * Construct an ObjectPool. + */ + ObjectPool( + const allocator_type & alloc = allocator_type()) + : collection_(alloc) + { + } + + const _Collection & collection() const noexcept + { + return collection_; + } + + _Collection & collection() noexcept + { + return collection_; + } + + template + value_type get( + _DefaultGetter _Default) + { + if (collection_.empty()) { + return _Default(); + } + + value_type ret = collection_.back(); + collection_.pop_back(); + return ret; + } + + template + void put( + _Valty &&... _Val) + { + collection_.emplace_back(std::forward<_Valty>(_Val)...); + } + +private: + _Collection collection_; +}; + +} // namespace common_content_filter + +#endif // OBJECTPOOL_HPP_ diff --git a/common_content_filter/src/Utilities.cpp b/common_content_filter/src/Utilities.cpp new file mode 100644 index 0000000..d83b763 --- /dev/null +++ b/common_content_filter/src/Utilities.cpp @@ -0,0 +1,55 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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 "Utilities.hpp" + +#include +#include +#include +#include +#include +#include + +#include "Log.hpp" + +const rosidl_message_type_support_t * +get_type_support_introspection( + const rosidl_message_type_support_t * type_support) +{ + const rosidl_message_type_support_t * type_support_introspection = + get_message_typesupport_handle( + type_support, rosidl_typesupport_introspection_c__identifier); + if (nullptr == type_support_introspection) { + rcutils_error_string_t prev_error_string = rcutils_get_error_string(); + rcutils_reset_error(); + + type_support_introspection = + get_message_typesupport_handle( + type_support, + rosidl_typesupport_introspection_cpp::typesupport_identifier); + if (nullptr == type_support_introspection) { + rcutils_error_string_t error_string = rcutils_get_error_string(); + rcutils_reset_error(); + logError( + SQLFILTER, + "Type support not from this implementation. Got:\n" + << " " << prev_error_string.str << "\n" + << " " << error_string.str << "\n" + << "while fetching it"); + return nullptr; + } + } + + return type_support_introspection; +} diff --git a/common_content_filter/src/Utilities.hpp b/common_content_filter/src/Utilities.hpp new file mode 100644 index 0000000..82f901c --- /dev/null +++ b/common_content_filter/src/Utilities.hpp @@ -0,0 +1,24 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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. + +#ifndef UTILITIES_HPP_ +#define UTILITIES_HPP_ + +#include + +const rosidl_message_type_support_t * +get_type_support_introspection( + const rosidl_message_type_support_t * type_support); + +#endif // UTILITIES_HPP_ diff --git a/common_content_filter/src/api.cpp b/common_content_filter/src/api.cpp new file mode 100644 index 0000000..bde695f --- /dev/null +++ b/common_content_filter/src/api.cpp @@ -0,0 +1,369 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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 "common_content_filter/api.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "FilterFactory.hpp" +#include "Log.hpp" +#include "Utilities.hpp" + + +namespace common_content_filter +{ + +const int MAGIC = 0x434654; // 'C','F','T' + +using FilterFactory = common_content_filter::SQLFilter::FilterFactory; +using IContentFilter = common_content_filter::IContentFilter; + +FilterFactory * +get_common_content_filter_factory() +{ + static FilterFactory content_filter_factory; + return &content_filter_factory; +} + +template +auto +get_message( + const rosidl_message_type_support_t * type_support_introspection, + MessageInitialization message_initialization) +{ + const MembersType * members = + static_cast(type_support_introspection->data); + if (!members) { + throw std::runtime_error("The data in the type support introspection is invalid."); + } + + auto msg = std::unique_ptr>( + malloc(members->size_of_), + [members](void * msg_ptr) { + members->fini_function(msg_ptr); + free(msg_ptr); + }); + if (msg) { + members->init_function(msg.get(), message_initialization); + } + + return msg; +} + +auto get_message_buffer(const rosidl_message_type_support_t * type_support) +{ + const rosidl_message_type_support_t * type_support_introspection = + get_type_support_introspection(type_support); + if (!type_support_introspection) { + throw std::runtime_error("failed to get type support introspection"); + } + + if (type_support_introspection->typesupport_identifier == + rosidl_typesupport_introspection_c__identifier) + { + return get_message( + type_support_introspection, ROSIDL_RUNTIME_C_MSG_INIT_ZERO); + } else { + return get_message( + type_support_introspection, rosidl_runtime_cpp::MessageInitialization::ZERO); + } +} + +class ContentFilterWrapper +{ +public: + explicit ContentFilterWrapper(const rosidl_message_type_support_t * type_support) + : type_support_(type_support) + {} + + ~ContentFilterWrapper() + { + std::lock_guard lock(mutex_); + if (filter_instance_) { + FilterFactory::ReturnCode_t ret = + get_common_content_filter_factory()->delete_content_filter(filter_instance_); + if (ret != FilterFactory::RETCODE_OK) { + logError(SQLFILTER, "Failed to delete content filter: " << ret); + } + + filter_instance_ = nullptr; + } + } + + bool evaluate(void * ros_data, bool serialized) + { + std::lock_guard lock(mutex_); + if (!filter_instance_) { + logWarning(SQLFILTER, "Common content filter is not set"); + return true; + } + + if (serialized) { + const rmw_serialized_message_t * serialized_message = + (const rmw_serialized_message_t *)ros_data; + if (!deserialized_buffer_) { + deserialized_buffer_ = get_message_buffer(type_support_); + } + rmw_ret_t rmw_ret = + rmw_deserialize(serialized_message, type_support_, deserialized_buffer_.get()); + ros_data = deserialized_buffer_.get(); + if (rmw_ret != RMW_RET_OK) { + logError(SQLFILTER, "Failed to deserialize message"); + return false; + } + } + + return filter_instance_->evaluate(ros_data); + } + + bool set_filter_expression( + const std::string & filter_expression, + const std::vector & expression_parameters) + { + std::lock_guard lock(mutex_); + const char * tip = (filter_instance_ == nullptr) ? "create" : "set"; + FilterFactory::ReturnCode_t ret = get_common_content_filter_factory()->create_content_filter( + type_support_, + filter_expression.c_str(), + expression_parameters, + filter_instance_); + if (ret != FilterFactory::RETCODE_OK) { + logError(SQLFILTER, "failed to " << tip << " content filter, error code: " << ret); + return false; + } + + filter_expression_ = filter_expression; + expression_parameters_ = expression_parameters; + + return true; + } + + bool get_filter_expression( + std::string & filter_expression, + std::vector & expression_parameters) + { + std::lock_guard lock(mutex_); + + if (!filter_instance_) { + logError(SQLFILTER, "content filter instance is not created"); + return false; + } + + filter_expression = filter_expression_; + expression_parameters = expression_parameters_; + + return true; + } + + IContentFilter * filter_instance() + { + return filter_instance_; + } + + bool is_enabled() + { + return filter_instance_ != nullptr; + } + + int magic() + { + return magic_; + } + +private: + const int magic_ = MAGIC; + const rosidl_message_type_support_t * type_support_; + std::unique_ptr> deserialized_buffer_ = nullptr; + IContentFilter * filter_instance_ = nullptr; + std::string filter_expression_; + std::vector expression_parameters_; + std::mutex mutex_; +}; + + +common_content_filter::ContentFilterWrapper * validate(void * instance) +{ + auto content_filter_wrapper = + static_cast(instance); + if (!content_filter_wrapper || content_filter_wrapper->magic() != common_content_filter::MAGIC) { + logError(SQLFILTER, "Invalid instance"); + return nullptr; + } + return content_filter_wrapper; +} + +} // namespace common_content_filter + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void * +common_content_filter_create(const rosidl_message_type_support_t * type_support) +{ + return new common_content_filter::ContentFilterWrapper(type_support); +} + +bool +common_content_filter_is_enabled(void * instance) +{ + common_content_filter::ContentFilterWrapper * content_filter_wrapper = + common_content_filter::validate(instance); + if (!content_filter_wrapper) { + return false; + } + + return content_filter_wrapper->is_enabled(); +} + +bool +common_content_filter_evaluate(void * instance, void * ros_data, bool serialized) +{ + common_content_filter::ContentFilterWrapper * content_filter_wrapper = + common_content_filter::validate(instance); + if (!content_filter_wrapper) { + return false; + } + + if (!ros_data) { + logError(SQLFILTER, "Invalid arguments"); + return false; + } + + bool ret = false; + try { + ret = content_filter_wrapper->evaluate(ros_data, serialized); + } catch (const std::runtime_error & e) { + logError(SQLFILTER, "Failed to evaluate: " << e.what()); + } + + return ret; +} + +bool +common_content_filter_set( + void * instance, + const rmw_subscription_content_filter_options_t * options +) +{ + common_content_filter::ContentFilterWrapper * content_filter_wrapper = + common_content_filter::validate(instance); + if (!content_filter_wrapper) { + return false; + } + + if (!options) { + logError(SQLFILTER, "Invalid arguments"); + return false; + } + + std::vector expression_parameters; + for (size_t i = 0; i < options->expression_parameters.size; ++i) { + expression_parameters.push_back(options->expression_parameters.data[i]); + } + + bool ret = false; + try { + ret = content_filter_wrapper->set_filter_expression( + options->filter_expression, expression_parameters + ); + } catch (const std::runtime_error & e) { + logError(SQLFILTER, "Failed to create content filter: " << e.what()); + } + + return ret; +} + +bool +common_content_filter_get( + void * instance, + rcutils_allocator_t * allocator, + rmw_subscription_content_filter_options_t * options +) +{ + common_content_filter::ContentFilterWrapper * content_filter_wrapper = + common_content_filter::validate(instance); + if (!content_filter_wrapper) { + return false; + } + + if (!allocator || !options) { + logError(SQLFILTER, "Invalid arguments"); + return false; + } + + std::string filter_expression; + std::vector expression_parameters; + bool ret = content_filter_wrapper->get_filter_expression( + filter_expression, + expression_parameters + ); + if (!ret) { + return false; + } + + std::vector string_array; + for (size_t i = 0; i < expression_parameters.size(); ++i) { + string_array.push_back(expression_parameters[i].c_str()); + } + + rmw_ret_t rmw_ret = rmw_subscription_content_filter_options_set( + filter_expression.c_str(), + string_array.size(), + string_array.data(), + allocator, + options + ); + + if (rmw_ret != RMW_RET_OK) { + logError(SQLFILTER, rmw_get_error_string().str); + rmw_reset_error(); + return false; + } + + return true; +} + +void +common_content_filter_destroy(void * instance) +{ + common_content_filter::ContentFilterWrapper * content_filter_wrapper = + common_content_filter::validate(instance); + if (!content_filter_wrapper) { + return; + } + + delete content_filter_wrapper; +} + +#ifdef __cplusplus +} +#endif diff --git a/common_content_filter/test/test_api.cpp b/common_content_filter/test/test_api.cpp new file mode 100644 index 0000000..862d698 --- /dev/null +++ b/common_content_filter/test/test_api.cpp @@ -0,0 +1,449 @@ +// Copyright 2022 Sony Group Corporation +// +// Licensed 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 +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "common_content_filter/api.h" + +class TestAPIBase +{ +protected: + bool set_options( + const char * filter_expression, + size_t expression_parameter_argc, + const char * expression_parameter_argv[]) + { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rmw_subscription_content_filter_options_t options = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_EQ( + RMW_RET_OK, + rmw_subscription_content_filter_options_init( + filter_expression, + expression_parameter_argc, + expression_parameter_argv, + &allocator, + &options) + ); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rmw_subscription_content_filter_options_fini(&options, &allocator); + }); + + return common_content_filter_set(instance, &options); + } + + void * instance; + const rosidl_message_type_support_t * type_support; +}; + +class TestCommonContentFilterAPI : public ::testing::Test, public TestAPIBase +{ +protected: + void SetUp() + { + type_support = ROSIDL_GET_MSG_TYPE_SUPPORT(test_msgs, msg, BasicTypes); + instance = common_content_filter_create(type_support); + EXPECT_NE(instance, nullptr); + } + + void TearDown() + { + common_content_filter_destroy(instance); + } + +protected: + const char * filter_expression = "int32_value = %0"; + std::vector expression_parameter = {"4"}; +}; + +TEST_F(TestCommonContentFilterAPI, is_enabled) { + EXPECT_FALSE(common_content_filter_is_enabled(nullptr)); + EXPECT_FALSE(common_content_filter_is_enabled(instance)); + EXPECT_TRUE( + set_options(filter_expression, expression_parameter.size(), expression_parameter.data())); + EXPECT_TRUE(common_content_filter_is_enabled(instance)); +} + +TEST_F(TestCommonContentFilterAPI, evaluate) { + EXPECT_FALSE(common_content_filter_evaluate(nullptr, nullptr, false)); + EXPECT_FALSE(common_content_filter_evaluate(instance, nullptr, false)); + EXPECT_FALSE(common_content_filter_evaluate(nullptr, nullptr, true)); + EXPECT_FALSE(common_content_filter_evaluate(instance, nullptr, true)); + + test_msgs__msg__BasicTypes msg; + test_msgs__msg__BasicTypes__init(&msg); + msg.int32_value = 3; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + test_msgs__msg__BasicTypes__fini(&msg); + }); + + // test serialized message + rmw_serialized_message_t serialized_message = + rmw_get_zero_initialized_serialized_message(); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + EXPECT_EQ( + RMW_RET_OK, + rmw_serialized_message_init(&serialized_message, 1U, &allocator) + ); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rmw_serialized_message_fini(&serialized_message); + }); + + EXPECT_EQ( + RMW_RET_OK, + rmw_serialize(&msg, type_support, &serialized_message) + ); + + // no filter setting, expect true + EXPECT_TRUE(common_content_filter_evaluate(instance, &msg, false)); + EXPECT_TRUE(common_content_filter_evaluate(instance, &serialized_message, true)); + // after setting filter with "int32_value = 4" + EXPECT_TRUE( + set_options(filter_expression, expression_parameter.size(), expression_parameter.data())); + // expect msg(int32_value = 3) return false + EXPECT_FALSE(common_content_filter_evaluate(instance, &msg, false)); + EXPECT_FALSE(common_content_filter_evaluate(instance, &serialized_message, true)); + // update msg with 4 + msg.int32_value = 4; + EXPECT_EQ( + RMW_RET_OK, + rmw_serialize(&msg, type_support, &serialized_message) + ); + + EXPECT_TRUE(common_content_filter_evaluate(instance, &msg, false)); + EXPECT_TRUE(common_content_filter_evaluate(instance, &serialized_message, true)); +} + +TEST_F(TestCommonContentFilterAPI, set) { + EXPECT_FALSE(common_content_filter_set(nullptr, nullptr)); + EXPECT_FALSE(common_content_filter_set(instance, nullptr)); + EXPECT_TRUE( + set_options(filter_expression, expression_parameter.size(), expression_parameter.data())); + + const char * filter_expression_error = "error_int32_value = %0"; + EXPECT_FALSE( + set_options(filter_expression_error, expression_parameter.size(), expression_parameter.data())); +} + +TEST_F(TestCommonContentFilterAPI, get) { + EXPECT_FALSE(common_content_filter_get(nullptr, nullptr, nullptr)); + EXPECT_FALSE(common_content_filter_get(instance, nullptr, nullptr)); + EXPECT_TRUE( + set_options(filter_expression, expression_parameter.size(), expression_parameter.data())); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + EXPECT_FALSE(common_content_filter_get(instance, &allocator, nullptr)); + + rmw_subscription_content_filter_options_t options = + rmw_get_zero_initialized_content_filter_options(); + EXPECT_TRUE(common_content_filter_get(instance, &allocator, &options)); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rmw_subscription_content_filter_options_fini(&options, &allocator); + }); + + EXPECT_STREQ(options.filter_expression, filter_expression); + ASSERT_EQ(expression_parameter.size(), options.expression_parameters.size); + for (size_t i = 0; i < expression_parameter.size(); ++i) { + EXPECT_STREQ(options.expression_parameters.data[i], expression_parameter[i]); + } +} + +class TestComplexMsgCommonContentFilterAPI : public ::testing::Test, public TestAPIBase +{ +protected: + void SetUp() + { + type_support = + rosidl_typesupport_cpp::get_message_type_support_handle< + test_content_filter_msgs::msg::Complex>(); + + instance = common_content_filter_create(type_support); + EXPECT_NE(instance, nullptr); + } + + void TearDown() + { + common_content_filter_destroy(instance); + } +}; + +TEST_F(TestComplexMsgCommonContentFilterAPI, set_and_evaluate) { + // default is zero + test_msgs::msg::BasicTypes basic_types_data_zero; + + // set member data with one + test_msgs::msg::BasicTypes basic_types_data_one; + basic_types_data_one.bool_value = true; + basic_types_data_one.byte_value = 1; + basic_types_data_one.char_value = 1; + basic_types_data_one.float32_value = 1.0f; + basic_types_data_one.float64_value = 1.0; + basic_types_data_one.int8_value = 1; + basic_types_data_one.uint8_value = 1; + basic_types_data_one.int16_value = 1; + basic_types_data_one.uint16_value = 1; + basic_types_data_one.int32_value = 1l; + basic_types_data_one.uint32_value = 1ul; + basic_types_data_one.int64_value = 1ll; + basic_types_data_one.uint64_value = 1ull; + + test_content_filter_msgs::msg::Basic basic_zero_one; + basic_zero_one.names = { + "basic_zero_one_first_name", // data.basic_array[0].names[0] + "basic_zero_one_second_name" // data.basic_array[0].names[1] + }; + basic_zero_one.basic_types = { + basic_types_data_zero, // data.basic_array[0].basic_types[0] is 0 + basic_types_data_one // data.basic_array[0].basic_types[1] is 1 + }; + basic_zero_one.unbounded_int32_data = {0, 1}; + basic_zero_one.bounded_float64_data = {0, 1}; + + test_content_filter_msgs::msg::Basic basic_one_zero; + basic_one_zero.names = { + "basic_one_zero_first_name", // data.basic_array[1].names[0] + "basic_one_zero_second_name" // data.basic_array[1].names[1] + }; + basic_one_zero.basic_types = { + basic_types_data_one, // data.basic_array[1].basic_types[0] is 1 + basic_types_data_zero // data.basic_array[1].basic_types[1] is 0 + }; + basic_one_zero.unbounded_int32_data = {1, 0}; + basic_one_zero.bounded_float64_data = {1, 0}; + + test_content_filter_msgs::msg::Complex msg; + msg.data.basic_array = { + basic_zero_one, + basic_one_zero + }; + msg.data.names = { + "intermedia_first_name", // data.names[0] + "intermedia_second_name" // data.names[1] + }; + + msg.name = "complex_name"; // name + + // no filter, expect the true value by evaluating a message + EXPECT_TRUE(common_content_filter_evaluate(instance, &msg, false)); + + struct Info + { + const char * filter_expression; + std::vector expression_parameter; + bool set_expectation; + bool evaluate_expectation; + }; + + std::vector expectation = { + // name with string or string array + {"name=%0", {"'complex_name'"}, true, true}, + {"name=%0", {"'not_complex_name'"}, true, false}, + + {"data.names[0]=%0", {"'intermedia_first_name'"}, true, true}, + {"data.names[0]=%0", {"'intermedia_second_name'"}, true, false}, + {"data.names[1]=%0", {"'intermedia_first_name'"}, true, false}, + {"data.names[1]=%0", {"'intermedia_second_name'"}, true, true}, + + {"data.basic_array[0].names[0]=%0", {"'basic_zero_one_first_name'"}, true, true}, + {"data.basic_array[0].names[0]=%0", {"'basic_zero_one_second_name'"}, true, false}, + {"data.basic_array[0].names[1]=%0", {"'basic_zero_one_first_name'"}, true, false}, + {"data.basic_array[0].names[1]=%0", {"'basic_zero_one_second_name'"}, true, true}, + + {"data.basic_array[1].names[0]=%0", {"'basic_one_zero_first_name'"}, true, true}, + {"data.basic_array[1].names[0]=%0", {"'basic_one_zero_second_name'"}, true, false}, + {"data.basic_array[1].names[1]=%0", {"'basic_one_zero_first_name'"}, true, false}, + {"data.basic_array[1].names[1]=%0", {"'basic_one_zero_second_name'"}, true, true}, + + {"name=%0 and data.names[0]=%1", {"'complex_name'", "'intermedia_first_name'"}, true, true}, + {"name=%0 and data.names[0]=%1", {"'not_complex_name'", "'intermedia_first_name'"}, true, + false}, + {"name=%0 or data.names[0]=%1", {"'complex_name'", "'intermedia_first_name'"}, true, true}, + {"name=%0 or data.names[0]=%1", {"'not_complex_name'", "'intermedia_first_name'"}, true, true}, + {"name=%0 or data.names[0]=%1", {"'complex_name'", "'intermedia_second_name'"}, true, true}, + + // basic types array + // [0] [0] + {"data.basic_array[0].basic_types[0].bool_value=%0", {"false"}, true, true}, + {"data.basic_array[0].basic_types[0].byte_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].char_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].float32_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].float64_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].int8_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].uint8_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].int16_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].uint16_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].int32_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].uint32_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].int64_value=%0", {"0"}, true, true}, + {"data.basic_array[0].basic_types[0].uint64_value=%0", {"0"}, true, true}, + {"data.basic_array[0].unbounded_int32_data[0]=%0", {"0"}, true, true}, + {"data.basic_array[0].bounded_float64_data[0]=%0", {"0"}, true, true}, + + {"data.basic_array[0].basic_types[0].bool_value=%0", {"true"}, true, false}, + {"data.basic_array[0].basic_types[0].byte_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].char_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].float32_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].float64_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].int8_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].uint8_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].int16_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].uint16_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].int32_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].uint32_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].int64_value=%0", {"1"}, true, false}, + {"data.basic_array[0].basic_types[0].uint64_value=%0", {"1"}, true, false}, + {"data.basic_array[0].unbounded_int32_data[0]=%0", {"1"}, true, false}, + {"data.basic_array[0].bounded_float64_data[0]=%0", {"1"}, true, false}, + + // [0] [1] + {"data.basic_array[0].basic_types[1].bool_value=%0", {"false"}, true, false}, + {"data.basic_array[0].basic_types[1].byte_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].char_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].float32_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].float64_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].int8_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].uint8_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].int16_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].uint16_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].int32_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].uint32_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].int64_value=%0", {"0"}, true, false}, + {"data.basic_array[0].basic_types[1].uint64_value=%0", {"0"}, true, false}, + {"data.basic_array[0].unbounded_int32_data[1]=%0", {"0"}, true, false}, + {"data.basic_array[0].bounded_float64_data[1]=%0", {"0"}, true, false}, + + {"data.basic_array[0].basic_types[1].bool_value=%0", {"true"}, true, true}, + {"data.basic_array[0].basic_types[1].byte_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].char_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].float32_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].float64_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].int8_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].uint8_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].int16_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].uint16_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].int32_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].uint32_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].int64_value=%0", {"1"}, true, true}, + {"data.basic_array[0].basic_types[1].uint64_value=%0", {"1"}, true, true}, + {"data.basic_array[0].unbounded_int32_data[1]=%0", {"1"}, true, true}, + {"data.basic_array[0].bounded_float64_data[1]=%0", {"1"}, true, true}, + + // [1][0] + {"data.basic_array[1].basic_types[0].bool_value=%0", {"false"}, true, false}, + {"data.basic_array[1].basic_types[0].byte_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].char_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].float32_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].float64_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].int8_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].uint8_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].int16_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].uint16_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].int32_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].uint32_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].int64_value=%0", {"0"}, true, false}, + {"data.basic_array[1].basic_types[0].uint64_value=%0", {"0"}, true, false}, + {"data.basic_array[1].unbounded_int32_data[0]=%0", {"0"}, true, false}, + {"data.basic_array[1].bounded_float64_data[0]=%0", {"0"}, true, false}, + + {"data.basic_array[1].basic_types[0].bool_value=%0", {"true"}, true, true}, + {"data.basic_array[1].basic_types[0].byte_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].char_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].float32_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].float64_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].int8_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].uint8_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].int16_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].uint16_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].int32_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].uint32_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].int64_value=%0", {"1"}, true, true}, + {"data.basic_array[1].basic_types[0].uint64_value=%0", {"1"}, true, true}, + {"data.basic_array[1].unbounded_int32_data[0]=%0", {"1"}, true, true}, + {"data.basic_array[1].bounded_float64_data[0]=%0", {"1"}, true, true}, + + // [1][1] + {"data.basic_array[1].basic_types[1].bool_value=%0", {"false"}, true, true}, + {"data.basic_array[1].basic_types[1].byte_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].char_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].float32_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].float64_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].int8_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].uint8_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].int16_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].uint16_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].int32_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].uint32_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].int64_value=%0", {"0"}, true, true}, + {"data.basic_array[1].basic_types[1].uint64_value=%0", {"0"}, true, true}, + {"data.basic_array[1].unbounded_int32_data[1]=%0", {"0"}, true, true}, + {"data.basic_array[1].bounded_float64_data[1]=%0", {"0"}, true, true}, + + {"data.basic_array[1].basic_types[1].bool_value=%0", {"true"}, true, false}, + {"data.basic_array[1].basic_types[1].byte_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].char_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].float32_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].float64_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].int8_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].uint8_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].int16_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].uint16_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].int32_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].uint32_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].int64_value=%0", {"1"}, true, false}, + {"data.basic_array[1].basic_types[1].uint64_value=%0", {"1"}, true, false}, + {"data.basic_array[1].unbounded_int32_data[1]=%0", {"1"}, true, false}, + {"data.basic_array[1].bounded_float64_data[1]=%0", {"1"}, true, false}, + + // some other cases + // bad field name + {"error_name=%0", {"'complex_name'"}, false, true}, + {"errordata.names[0]=%0", {"'intermedia_first_name'"}, false, true}, + + // unbound case + {"data.names[10]=%0", {"'unbound_name'"}, true, false}, + + // bound case, the size of bounded_float64_data is 2 + {"data.basic_array[0].bounded_float64_data[10]=%0", {"0"}, false, true}, + + // TODO(iuhilnehc-ynos): if bugs found, add new test cases and fix source code. + }; + + for (auto & item : expectation) { + EXPECT_EQ( + item.set_expectation, + set_options( + item.filter_expression, + item.expression_parameter.size(), + item.expression_parameter.data())) + << "Set error happened by the filter expression:" << item.filter_expression; + EXPECT_EQ(item.evaluate_expectation, common_content_filter_evaluate(instance, &msg, false)) + << "Evaluate error happened by the filter expression:" << item.filter_expression; + + // reset + EXPECT_TRUE(set_options("", 0, nullptr)); + } +} diff --git a/tao_pegtl_vendor/CMakeLists.txt b/tao_pegtl_vendor/CMakeLists.txt new file mode 100644 index 0000000..cdf9e28 --- /dev/null +++ b/tao_pegtl_vendor/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required(VERSION 3.5) + +project(tao_pegtl_vendor) + +find_package(ament_cmake REQUIRED) + +macro(build_tao_pegtl) + set(extra_cmake_args) + if(DEFINED CMAKE_BUILD_TYPE) + list(APPEND extra_cmake_args -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) + endif() + if(DEFINED CMAKE_TOOLCHAIN_FILE) + list(APPEND extra_cmake_args "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") + if(ANDROID) + if(DEFINED ANDROID_ABI) + list(APPEND extra_cmake_args "-DANDROID_ABI=${ANDROID_ABI}") + endif() + if(DEFINED ANDROID_CPP_FEATURES) + list(APPEND extra_cmake_args "-DANDROID_CPP_FEATURES=${ANDROID_CPP_FEATURES}") + endif() + if(DEFINED ANDROID_FUNCTION_LEVEL_LINKING) + list(APPEND extra_cmake_args "-DANDROID_FUNCTION_LEVEL_LINKING=${ANDROID_FUNCTION_LEVEL_LINKING}") + endif() + if(DEFINED ANDROID_NATIVE_API_LEVEL) + list(APPEND extra_cmake_args "-DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL}") + endif() + if(DEFINED ANDROID_NDK) + list(APPEND extra_cmake_args "-DANDROID_NDK=${ANDROID_NDK}") + endif() + if(DEFINED ANDROID_STL) + list(APPEND extra_cmake_args "-DANDROID_STL=${ANDROID_STL}") + endif() + if(DEFINED ANDROID_TOOLCHAIN_NAME) + list(APPEND extra_cmake_args "-DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME}") + endif() + endif() + else() + list(APPEND extra_cmake_args "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") + endif() + list(APPEND extra_cmake_args "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}") + list(APPEND extra_cmake_args "-DPEGTL_BUILD_TESTS=OFF") + list(APPEND extra_cmake_args "-DPEGTL_BUILD_EXAMPLES=OFF") + + include(ExternalProject) + + # Get pegtl 2.8.3 + externalproject_add(pegtl-2.8.3 + URL https://github.com/taocpp/PEGTL/archive/refs/tags/2.8.3.tar.gz + URL_MD5 28b3c455d9ec392dd4230402383a8c6f + TIMEOUT 60 + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_install + ${extra_cmake_args} + ) + + # The external project will install to the build folder, but we'll install that on make install. + install( + DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_install/ + DESTINATION + ${CMAKE_INSTALL_PREFIX} + USE_SOURCE_PERMISSIONS + ) +endmacro() + +# build pegtl 2.8.3 if it's not available in the system or ros2 environment +find_package(pegtl QUIET) +if(NOT pegtl_FOUND OR NOT "${pegtl_VERSION}" VERSION_EQUAL 2.8.3) + build_tao_pegtl() +endif() + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +# this ensures that the package has an environment hook setting the PATH +ament_package( + CONFIG_EXTRAS "tao_pegtl_vendor-extras.cmake" +) diff --git a/tao_pegtl_vendor/CONTRIBUTING.md b/tao_pegtl_vendor/CONTRIBUTING.md new file mode 100644 index 0000000..cfba094 --- /dev/null +++ b/tao_pegtl_vendor/CONTRIBUTING.md @@ -0,0 +1,18 @@ +Any contribution that you make to this repository will +be under the Apache 2 License, as dictated by that +[license](http://www.apache.org/licenses/LICENSE-2.0.html): + +~~~ +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. +~~~ + +Contributors must sign-off each commit by adding a `Signed-off-by: ...` +line to commit messages to certify that they have the right to submit +the code they are contributing to the project according to the +[Developer Certificate of Origin (DCO)](https://developercertificate.org/). diff --git a/tao_pegtl_vendor/LICENSE b/tao_pegtl_vendor/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/tao_pegtl_vendor/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/tao_pegtl_vendor/README.md b/tao_pegtl_vendor/README.md new file mode 100644 index 0000000..0148591 --- /dev/null +++ b/tao_pegtl_vendor/README.md @@ -0,0 +1,2 @@ +# tao_pegtl_vendor +CMake shim over the taocpp PEGTL library: https://github.com/taocpp/PEGTL.git diff --git a/tao_pegtl_vendor/package.xml b/tao_pegtl_vendor/package.xml new file mode 100644 index 0000000..79301a7 --- /dev/null +++ b/tao_pegtl_vendor/package.xml @@ -0,0 +1,24 @@ + + + + tao_pegtl_vendor + 0.0.1 + + Wrapper around pegtl, providing nothing but a dependency on pegtl, on some systems. + On others, it provides an ExternalProject build of spdlog. + + chenlh + Apache License 2.0 + MIT + + https://github.com/taocpp/PEGTL + + ament_cmake + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/tao_pegtl_vendor/tao_pegtl_vendor-extras.cmake b/tao_pegtl_vendor/tao_pegtl_vendor-extras.cmake new file mode 100644 index 0000000..914b75d --- /dev/null +++ b/tao_pegtl_vendor/tao_pegtl_vendor-extras.cmake @@ -0,0 +1,18 @@ +# Copyright 2022 Sony Corporation +# +# Licensed 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. + +# use the fixed version to prevent future compilation issues +# the number version 2.8.3 is from CMakeLists.txt +find_package(pegtl 2.8.3 EXACT REQUIRED) +list(APPEND tao_pegtl_vendor_TARGETS taocpp::pegtl) diff --git a/test_content_filter_msgs/CMakeLists.txt b/test_content_filter_msgs/CMakeLists.txt new file mode 100644 index 0000000..4bbe6fc --- /dev/null +++ b/test_content_filter_msgs/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.5) + +project(test_content_filter_msgs) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(test_msgs REQUIRED) + +rosidl_generate_interfaces(test_content_filter_msgs + "msg/Basic.msg" + "msg/Complex.msg" + "msg/Intermediate.msg" + DEPENDENCIES test_msgs +) + +ament_package() diff --git a/test_content_filter_msgs/README.md b/test_content_filter_msgs/README.md new file mode 100644 index 0000000..12f6bf4 --- /dev/null +++ b/test_content_filter_msgs/README.md @@ -0,0 +1,2 @@ +# test_content_filter_msgs +A complex message to test common content filter api diff --git a/test_content_filter_msgs/msg/Basic.msg b/test_content_filter_msgs/msg/Basic.msg new file mode 100644 index 0000000..523d0df --- /dev/null +++ b/test_content_filter_msgs/msg/Basic.msg @@ -0,0 +1,4 @@ +string[] names +test_msgs/BasicTypes[] basic_types +int32[] unbounded_int32_data +float64[2] bounded_float64_data diff --git a/test_content_filter_msgs/msg/Complex.msg b/test_content_filter_msgs/msg/Complex.msg new file mode 100644 index 0000000..a07a86c --- /dev/null +++ b/test_content_filter_msgs/msg/Complex.msg @@ -0,0 +1,2 @@ +string name +Intermediate data diff --git a/test_content_filter_msgs/msg/Intermediate.msg b/test_content_filter_msgs/msg/Intermediate.msg new file mode 100644 index 0000000..2de8302 --- /dev/null +++ b/test_content_filter_msgs/msg/Intermediate.msg @@ -0,0 +1,2 @@ +Basic[] basic_array +string[] names \ No newline at end of file diff --git a/test_content_filter_msgs/package.xml b/test_content_filter_msgs/package.xml new file mode 100644 index 0000000..351d049 --- /dev/null +++ b/test_content_filter_msgs/package.xml @@ -0,0 +1,22 @@ + + + + test_content_filter_msgs + 0.0.1 + Complex messages for test common content filter api. + chenlh + Apache License 2.0 + + ament_cmake + + rosidl_default_generators + test_msgs + rosidl_default_runtime + test_msgs + + rosidl_interface_packages + + + ament_cmake + +