diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 1daba5dbc9798..7357167a3fcd8 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(Framework src/ArrowTableSlicingCache.cxx src/AnalysisDataModel.cxx src/AnalysisSupportHelpers.cxx + src/AnalysisTask.cxx src/ASoA.cxx src/ASoAHelpers.cxx src/AsyncQueue.cxx @@ -254,6 +255,7 @@ add_executable(o2-test-framework-core test/test_TimeParallelPipelining.cxx test/test_TimesliceIndex.cxx test/test_TypeTraits.cxx + test/test_TypeToTaskName.cxx test/test_TopologyPolicies.cxx test/test_Variants.cxx test/test_WorkflowHelpers.cxx diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 365c6b1d41692..fbd523c7b0c37 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -36,6 +36,9 @@ namespace o2::framework { +/// Convert a CamelCase task struct name to snake-case task name +std::string type_to_task_name(std::string_view const& camelCase); + /// A more familiar task API for the DPL analysis framework. /// This allows you to define your own tasks as subclasses /// of o2::framework::AnalysisTask and to pass them in the specification diff --git a/Framework/Core/src/AnalysisTask.cxx b/Framework/Core/src/AnalysisTask.cxx new file mode 100644 index 0000000000000..e88e6fbc6f041 --- /dev/null +++ b/Framework/Core/src/AnalysisTask.cxx @@ -0,0 +1,77 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 + +namespace o2::framework +{ +/// Convert a CamelCase task struct name to snake-case task name +std::string type_to_task_name(std::string_view const& camelCase) +{ + std::string result; + result.reserve(camelCase.size() * 2 + 2); + + // The first character is always -. + result += "-"; + result += static_cast(std::tolower(camelCase[0])); + + for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) { + if (std::isupper(*it) && *(it - 1) != '-') { + result += '-'; + } + result += static_cast(std::tolower(*it)); + } + // Post-process to consolidate common ALICE abbreviations + // Process backwards to handle patterns correctly + static const struct { + std::string_view pattern; + std::string_view replacement; + } abbreviations[] = { + {"-h-m-p-i-d", "-hmpid"}, + {"-e-m-c-a-l", "-emcal"}, + {"-e-m-c", "-emc"}, + {"-i-t-s", "-its"}, + {"-t-p-c", "-tpc"}, + {"-q-c-d", "-qcd"}, + {"-t-o-f", "-tof"}, + {"-t-r-d", "-trd"}, + {"-f-v0", "-fv0"}, + {"-q-a", "-qa"}, + {"-b-c", "-bc"}, + {"-q-c", "-qc"}}; + + std::string consolidated; + consolidated.reserve(result.size()); + + for (int i = result.size() - 1; i >= 0;) { + bool matched = false; + + for (const auto& abbr : abbreviations) { + int startPos = i - abbr.pattern.size() + 1; + if (startPos >= 0 && result.compare(startPos, abbr.pattern.size(), abbr.pattern.data()) == 0) { + consolidated.insert(0, abbr.replacement); + i = startPos - 1; + matched = true; + break; + } + } + + if (!matched) { + consolidated.insert(0, 1, result[i]); + --i; + } + } + if (consolidated[0] == '-') { + return std::string(consolidated.data() + 1); + } + + return consolidated; +} +} // namespace o2::framework diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx new file mode 100644 index 0000000000000..b7b440b13ecfd --- /dev/null +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -0,0 +1,65 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 +#include "Framework/AnalysisTask.h" +#include + +using namespace o2::framework; + +TEST_CASE("TypeIdHelpers_BasicConversion") +{ + // Basic CamelCase to snake-case conversion + REQUIRE((type_to_task_name(std::string_view("SimpleTask")) == "simple-task")); + REQUIRE((type_to_task_name(std::string_view("MyTask")) == "my-task")); + REQUIRE((type_to_task_name(std::string_view("Task")) == "task")); +} + +TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") +{ + // Test ALICE detector abbreviations + REQUIRE(type_to_task_name(std::string_view("ITSQA")) == "its-qa"); + REQUIRE(type_to_task_name(std::string_view("TPCQCTask")) == "tpc-qc-task"); + REQUIRE(type_to_task_name(std::string_view("EMCALQATask")) == "emcal-qa-task"); + REQUIRE(type_to_task_name(std::string_view("HMPIDTask")) == "hmpid-task"); + REQUIRE(type_to_task_name(std::string_view("ITSTPCTask")) == "its-tpc-task"); + REQUIRE(type_to_task_name(std::string_view("QCFV0Task")) == "qc-fv0-task"); +} + +TEST_CASE("TypeIdHelpers_QualityControlAbbreviations") +{ + // Test quality control abbreviations + REQUIRE(type_to_task_name(std::string_view("QATask")) == "qa-task"); + REQUIRE(type_to_task_name(std::string_view("QCTask")) == "qc-task"); + REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); +} + +TEST_CASE("TypeIdHelpers_ComplexNames") +{ + // Test complex combinations + REQUIRE(type_to_task_name(std::string_view("ITSQAAnalysisTask")) == "its-qa-analysis-task"); + REQUIRE(type_to_task_name(std::string_view("TPCEMCQCTask")) == "tpc-emc-qc-task"); + REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); +} + +TEST_CASE("TypeIdHelpers_EdgeCases") +{ + // Single character + REQUIRE(type_to_task_name(std::string_view("A")) == "a"); + + // All uppercase. BC is Bunch Crossing! + // + REQUIRE(type_to_task_name(std::string_view("ABC")) == "a-bc"); + REQUIRE(type_to_task_name(std::string_view("BC")) == "bc"); + + // Mixed with numbers (numbers are not uppercase, so no hyphens before them) + REQUIRE(type_to_task_name(std::string_view("Task123")) == "task123"); +} diff --git a/Framework/Foundation/include/Framework/TypeIdHelpers.h b/Framework/Foundation/include/Framework/TypeIdHelpers.h index 5eaac2151b909..1dc2464b40ec8 100644 --- a/Framework/Foundation/include/Framework/TypeIdHelpers.h +++ b/Framework/Foundation/include/Framework/TypeIdHelpers.h @@ -13,7 +13,6 @@ #define O2_FRAMEWORK_TYPEIDHELPERS_H_ #include -#include #if __cplusplus >= 202002L #include #endif @@ -82,22 +81,6 @@ struct TypeIdHelpers { } }; -/// Convert a CamelCase task struct name to snake-case task name -inline static std::string type_to_task_name(std::string_view& camelCase) -{ - std::ostringstream str; - str << static_cast(std::tolower(camelCase[0])); - - for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) { - if (std::isupper(*it) && *(it - 1) != '-') { - str << "-"; - } - str << static_cast(std::tolower(*it)); - } - - return str.str(); -} - } // namespace o2::framework #endif // O2_FRAMEWORK_TYPEIDHELPERS_H_