From 52072227c1164a7656de249022ba78644399d80e Mon Sep 17 00:00:00 2001 From: Giulio Eulisse Date: Tue, 16 Oct 2018 16:53:46 +0200 Subject: [PATCH] DPL: add query builder for DataDescriptors from a config string This can be used to e.g. allow the user to select which outputs to save from command line, or to write a InputSpec in a generic manner, without having to rely on the O2 Data Model descriptors. For the moment the syntax is very basic: //,<...> but we can complicate it as needed in later PRs. --- Framework/Core/CMakeLists.txt | 1 + .../include/Framework/DataDescriptorMatcher.h | 16 +++-- .../Framework/DataDescriptorQueryBuilder.h | 42 +++++++++++ .../Core/src/DataDescriptorQueryBuilder.cxx | 62 ++++++++++++++++ .../Core/test/test_DataDescriptorMatcher.cxx | 71 +++++++++++++++++-- 5 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 Framework/Core/include/Framework/DataDescriptorQueryBuilder.h create mode 100644 Framework/Core/src/DataDescriptorQueryBuilder.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index fc0cc39626332..7ebcd77f4b4e0 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -53,6 +53,7 @@ set(SRCS src/FairMQResizableBuffer.cxx src/GraphvizHelpers.cxx src/InputRecord.cxx + src/DataDescriptorQueryBuilder.cxx src/LifetimeHelpers.cxx src/LocalRootFileService.cxx src/LogParsingHelpers.cxx diff --git a/Framework/Core/include/Framework/DataDescriptorMatcher.h b/Framework/Core/include/Framework/DataDescriptorMatcher.h index bfbedf70aaf29..cadffd6385138 100644 --- a/Framework/Core/include/Framework/DataDescriptorMatcher.h +++ b/Framework/Core/include/Framework/DataDescriptorMatcher.h @@ -129,11 +129,13 @@ using Node = std::variant +#include +#include + +namespace o2 +{ +namespace framework +{ + +class DataDescriptorMatcher; + +/// Various utilities to manipulate InputSpecs +struct DataDescriptorQueryBuilder { + /// Creates an inputspec from a configuration @a config string with the + /// following grammar. + /// + /// string := [a-zA-Z0-9_]* + /// origin := string + /// description := string + /// subspec := [0-9]* + /// spec := origin/description/subspec + /// config := spec;spec;... + /// + /// Example for config: TPC/CLUSTER/0;ITS/TRACKS/1 + static std::shared_ptr buildFromKeepConfig(std::string const& config); +}; + +} // namespace framework +} // namespace o2 +#endif // o2_framework_DataDescriptorQueryBuilder_H_INCLUDED diff --git a/Framework/Core/src/DataDescriptorQueryBuilder.cxx b/Framework/Core/src/DataDescriptorQueryBuilder.cxx new file mode 100644 index 0000000000000..889a7ec41dc0f --- /dev/null +++ b/Framework/Core/src/DataDescriptorQueryBuilder.cxx @@ -0,0 +1,62 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/DataDescriptorQueryBuilder.h" +#include "Framework/DataDescriptorMatcher.h" + +#include +#include +#include +#include + +namespace o2 +{ +namespace framework +{ + +std::shared_ptr DataDescriptorQueryBuilder::buildFromKeepConfig(std::string const& config) +{ + static const std::regex specTokenRE(R"re((\w{1,4})/(\w{1,16})/(\d*))re"); + static const std::regex delimiter(","); + + std::sregex_token_iterator iter(config.begin(), + config.end(), + delimiter, + -1); + std::sregex_token_iterator end; + + std::unique_ptr result; + + for (; iter != end; ++iter) { + std::smatch m; + auto s = iter->str(); + std::regex_match(s, m, specTokenRE); + std::unique_ptr next; + auto newNode = std::make_unique( + DataDescriptorMatcher::Op::And, + OriginValueMatcher{ m[1] }, + std::make_unique( + DataDescriptorMatcher::Op::And, + DescriptionValueMatcher{ m[2] }, + SubSpecificationTypeValueMatcher{ m[3] })); + if (result.get() == nullptr) { + result = std::move(newNode); + } else { + next = std::move(std::make_unique(DataDescriptorMatcher::Op::Or, + std::move(result), + std::move(newNode))); + result = std::move(next); + } + } + return std::move(result); +} + +} // namespace framework +} // namespace o2 diff --git a/Framework/Core/test/test_DataDescriptorMatcher.cxx b/Framework/Core/test/test_DataDescriptorMatcher.cxx index bf07ee909eb46..9583b1c2bd66d 100644 --- a/Framework/Core/test/test_DataDescriptorMatcher.cxx +++ b/Framework/Core/test/test_DataDescriptorMatcher.cxx @@ -12,8 +12,10 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK -#include #include "Framework/DataDescriptorMatcher.h" +#include "Framework/DataDescriptorQueryBuilder.h" + +#include using namespace o2::framework; using namespace o2::header; @@ -46,14 +48,14 @@ BOOST_AUTO_TEST_CASE(TestSimpleMatching) header4.subSpecification = 0; DataDescriptorMatcher matcher{ - OriginValueMatcher{ "TPC" }, DataDescriptorMatcher::Op::And, + OriginValueMatcher{ "TPC" }, std::make_unique( - DescriptionValueMatcher{ "CLUSTERS" }, DataDescriptorMatcher::Op::And, + DescriptionValueMatcher{ "CLUSTERS" }, std::make_unique( - SubSpecificationTypeValueMatcher{ 1 }, DataDescriptorMatcher::Op::And, + SubSpecificationTypeValueMatcher{ 1 }, ConstantValueMatcher{ true })) }; @@ -64,8 +66,8 @@ BOOST_AUTO_TEST_CASE(TestSimpleMatching) BOOST_CHECK(matcher.match(header4) == false); DataDescriptorMatcher matcher1{ - OriginValueMatcher{ "TPC" }, DataDescriptorMatcher::Op::Or, + OriginValueMatcher{ "TPC" }, OriginValueMatcher{ "ITS" } }; @@ -76,8 +78,7 @@ BOOST_AUTO_TEST_CASE(TestSimpleMatching) BOOST_CHECK(matcher1.match(header4) == false); DataDescriptorMatcher matcher2{ - ConstantValueMatcher{ true }, - DataDescriptorMatcher::Op::And, + DataDescriptorMatcher::Op::Just, DescriptionValueMatcher{ "TRACKLET" } }; @@ -87,3 +88,59 @@ BOOST_AUTO_TEST_CASE(TestSimpleMatching) BOOST_CHECK(matcher2.match(header3) == false); BOOST_CHECK(matcher2.match(header4) == true); } + +BOOST_AUTO_TEST_CASE(TestQueryBuilder) +{ + DataHeader header0; + header0.dataOrigin = "TPC"; + header0.dataDescription = "CLUSTERS"; + header0.subSpecification = 1; + + DataHeader header1; + header1.dataOrigin = "ITS"; + header1.dataDescription = "TRACKLET"; + header1.subSpecification = 2; + + DataHeader header2; + header2.dataOrigin = "TPC"; + header2.dataDescription = "TRACKLET"; + header2.subSpecification = 1; + + DataHeader header3; + header3.dataOrigin = "TPC"; + header3.dataDescription = "CLUSTERS"; + header3.subSpecification = 0; + + DataHeader header4; + header4.dataOrigin = "TRD"; + header4.dataDescription = "TRACKLET"; + header4.subSpecification = 0; + + auto matcher1 = DataDescriptorQueryBuilder::buildFromKeepConfig("TPC/CLUSTERS/1"); + BOOST_CHECK(matcher1->match(header0) == true); + BOOST_CHECK(matcher1->match(header1) == false); + BOOST_CHECK(matcher1->match(header2) == false); + BOOST_CHECK(matcher1->match(header3) == false); + BOOST_CHECK(matcher1->match(header4) == false); + + auto matcher2 = DataDescriptorQueryBuilder::buildFromKeepConfig("ITS/TRACKLET/2"); + BOOST_CHECK(matcher2->match(header0) == false); + BOOST_CHECK(matcher2->match(header1) == true); + BOOST_CHECK(matcher2->match(header2) == false); + BOOST_CHECK(matcher2->match(header3) == false); + BOOST_CHECK(matcher2->match(header4) == false); + + auto matcher3 = DataDescriptorQueryBuilder::buildFromKeepConfig("TPC/CLUSTERS/1,ITS/TRACKLET/2"); + BOOST_CHECK(matcher3->match(header0) == true); + BOOST_CHECK(matcher3->match(header1) == true); + BOOST_CHECK(matcher3->match(header2) == false); + BOOST_CHECK(matcher3->match(header3) == false); + BOOST_CHECK(matcher3->match(header4) == false); + + auto matcher4 = DataDescriptorQueryBuilder::buildFromKeepConfig(""); + BOOST_CHECK(matcher4->match(header0) == false); + BOOST_CHECK(matcher4->match(header1) == false); + BOOST_CHECK(matcher4->match(header2) == false); + BOOST_CHECK(matcher4->match(header3) == false); + BOOST_CHECK(matcher4->match(header4) == false); +}