Skip to content

Commit 9e85643

Browse files
committed
DPL: provide a way to specify lifetime from text query
1 parent f93504a commit 9e85643

2 files changed

Lines changed: 121 additions & 19 deletions

File tree

Framework/Core/src/DataDescriptorQueryBuilder.cxx

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@
1717
#include <optional>
1818
#include <string>
1919
#include <vector>
20+
#include <iostream>
2021

2122
using namespace o2::framework::data_matcher;
2223

23-
namespace o2
24-
{
25-
namespace framework
24+
namespace o2::framework
2625
{
2726

2827
enum QueryBuilderState {
@@ -42,7 +41,13 @@ enum QueryBuilderState {
4241
IN_END_QUERY,
4342
IN_STRING,
4443
IN_NUMBER,
45-
IN_ERROR
44+
IN_ERROR,
45+
IN_BEGIN_ATTRIBUTES,
46+
IN_END_ATTRIBUTES,
47+
IN_BEGIN_KEY,
48+
IN_END_KEY,
49+
IN_BEGIN_VALUE,
50+
IN_END_VALUE
4651
};
4752

4853
std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
@@ -61,8 +66,11 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
6166
std::optional<std::string> currentBinding;
6267
std::optional<std::string> currentOrigin;
6368
std::optional<std::string> currentDescription;
69+
std::optional<std::string> currentKey;
70+
std::optional<std::string> currentValue;
6471
std::optional<header::DataHeader::SubSpecificationType> currentSubSpec;
6572
std::optional<uint64_t> currentTimeModulo;
73+
Lifetime currentLifetime = Lifetime::Timeframe;
6674
size_t currentNumber;
6775

6876
auto error = [&errorString, &states](std::string const& s) {
@@ -79,7 +87,7 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
7987
expectedSeparators = sep;
8088
};
8189

82-
auto assignLastStringMatch = [&next, &cur, &error, &pushState, &nodes](std::string const& what, size_t maxSize, std::optional<std::string>& s, QueryBuilderState nextState) {
90+
auto assignLastStringMatch = [&next, &cur, &error, &pushState](std::string const& what, int maxSize, std::optional<std::string>& s, QueryBuilderState nextState) {
8391
if ((next - cur == 0) || (next - cur > maxSize)) {
8492
error(what + " needs to be between 1 and " + std::to_string(maxSize) + " char long");
8593
return false;
@@ -95,7 +103,7 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
95103
return true;
96104
};
97105

98-
auto assignLastNumericMatch = [&next, &cur, &error, &pushState, &currentNumber](std::string const& what, auto& value,
106+
auto assignLastNumericMatch = [&next, &cur, &error, &pushState, &currentNumber](std::string const&, auto& value,
99107
QueryBuilderState nextState) {
100108
if ((next - cur == 0)) {
101109
error("number expected");
@@ -112,7 +120,9 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
112120
return true;
113121
};
114122

115-
auto buildMatchingTree = [&nodes](std::string const& binding) -> InputSpec {
123+
std::vector<ConfigParamSpec> attributes;
124+
125+
auto buildMatchingTree = [&nodes](std::string const& binding, std::vector<ConfigParamSpec> attributes) -> InputSpec {
116126
auto lastMatcher =
117127
std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Just,
118128
StartTimeValueMatcher(ContextRef{0}));
@@ -124,7 +134,13 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
124134
assert(lastMatcher.get() == nullptr);
125135
lastMatcher = std::move(tmp);
126136
}
127-
return InputSpec{binding, std::move(*lastMatcher.release())};
137+
Lifetime lifetime = Lifetime::Timeframe;
138+
for (auto& attribute : attributes) {
139+
if (attribute.name == "lifetime" && attribute.defaultValue.get<std::string>() == "condition") {
140+
lifetime = Lifetime::Condition;
141+
}
142+
}
143+
return InputSpec{binding, std::move(*lastMatcher.release()), lifetime, attributes};
128144
};
129145

130146
while (states.empty() == false) {
@@ -151,6 +167,7 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
151167
} break;
152168
case IN_BEGIN_MATCHER: {
153169
nodes.clear();
170+
attributes.clear();
154171
pushState(IN_BEGIN_BINDING);
155172
} break;
156173
case IN_BEGIN_BINDING: {
@@ -174,34 +191,58 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
174191
} break;
175192
case IN_BEGIN_ORIGIN: {
176193
pushState(IN_END_ORIGIN);
177-
token(IN_STRING, "/;");
194+
token(IN_STRING, "/;?");
178195
} break;
179196
case IN_END_ORIGIN: {
180-
if (assignLastStringMatch("origin", 4, currentOrigin, IN_BEGIN_DESCRIPTION)) {
197+
if (*next == '/' && assignLastStringMatch("origin", 4, currentOrigin, IN_BEGIN_DESCRIPTION)) {
198+
nodes.push_back(OriginValueMatcher{*currentOrigin});
199+
} else if (*next == ';' && assignLastStringMatch("origin", 4, currentOrigin, IN_END_MATCHER)) {
181200
nodes.push_back(OriginValueMatcher{*currentOrigin});
201+
} else if (*next == '?' && assignLastStringMatch("origin", 4, currentOrigin, IN_BEGIN_ATTRIBUTES)) {
202+
nodes.push_back(OriginValueMatcher{*currentOrigin});
203+
} else if (*next == '\0' && assignLastStringMatch("origin", 4, currentOrigin, IN_END_MATCHER)) {
204+
nodes.push_back(OriginValueMatcher{*currentOrigin});
205+
} else {
206+
error("origin needs to be between 1 and 4 char long");
182207
}
183208
} break;
184209
case IN_BEGIN_DESCRIPTION: {
185210
pushState(IN_END_DESCRIPTION);
186-
token(IN_STRING, "/;");
211+
token(IN_STRING, "/;?");
187212
} break;
188213
case IN_END_DESCRIPTION: {
189-
if (assignLastStringMatch("description", 16, currentDescription, IN_BEGIN_SUBSPEC)) {
214+
if (*next == '/' && assignLastStringMatch("description", 16, currentDescription, IN_BEGIN_SUBSPEC)) {
190215
nodes.push_back(DescriptionValueMatcher{*currentDescription});
216+
} else if (*next == ';' && assignLastStringMatch("description", 16, currentDescription, IN_END_MATCHER)) {
217+
nodes.push_back(DescriptionValueMatcher{*currentDescription});
218+
} else if (*next == '?' && assignLastStringMatch("description", 16, currentDescription, IN_BEGIN_ATTRIBUTES)) {
219+
nodes.push_back(DescriptionValueMatcher{*currentDescription});
220+
} else if (*next == '\0' && assignLastStringMatch("description", 16, currentDescription, IN_BEGIN_ATTRIBUTES)) {
221+
nodes.push_back(DescriptionValueMatcher{*currentDescription});
222+
} else {
223+
error("description needs to be between 1 and 16 char long");
191224
}
192225
} break;
193226
case IN_BEGIN_SUBSPEC: {
194227
pushState(IN_END_SUBSPEC);
195-
token(IN_NUMBER, ";%");
228+
token(IN_NUMBER, ";%?");
196229
} break;
197230
case IN_END_SUBSPEC: {
198-
if (assignLastNumericMatch("subspec", currentSubSpec, IN_BEGIN_TIMEMODULO)) {
231+
if (*next == '%' && assignLastNumericMatch("subspec", currentSubSpec, IN_BEGIN_TIMEMODULO)) {
232+
nodes.push_back(SubSpecificationTypeValueMatcher{*currentSubSpec});
233+
} else if (*next == '?' && assignLastNumericMatch("subspec", currentSubSpec, IN_BEGIN_ATTRIBUTES)) {
234+
nodes.push_back(SubSpecificationTypeValueMatcher{*currentSubSpec});
235+
} else if (*next == ';' && assignLastNumericMatch("subspec", currentSubSpec, IN_END_MATCHER)) {
236+
nodes.push_back(SubSpecificationTypeValueMatcher{*currentSubSpec});
237+
} else if (*next == '\0' && assignLastNumericMatch("subspec", currentSubSpec, IN_END_MATCHER)) {
199238
nodes.push_back(SubSpecificationTypeValueMatcher{*currentSubSpec});
239+
} else {
240+
error("Expected a number");
200241
}
201242
} break;
202243
case IN_BEGIN_TIMEMODULO: {
203244
pushState(IN_END_TIMEMODULO);
204-
token(IN_NUMBER, ";");
245+
token(IN_NUMBER, ";?");
205246
} break;
206247
case IN_END_TIMEMODULO: {
207248
assignLastNumericMatch("timemodulo", currentTimeModulo, IN_ERROR);
@@ -211,16 +252,62 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
211252
error("Remove trailing ;");
212253
continue;
213254
}
214-
result.push_back(buildMatchingTree(*currentBinding));
255+
result.push_back(buildMatchingTree(*currentBinding, attributes));
215256
if (*cur == '\0') {
216257
pushState(IN_END_QUERY);
217258
} else if (*cur == ';') {
218259
cur += 1;
219260
pushState(IN_BEGIN_MATCHER);
220261
} else {
221-
error("Unexpected character" + std::string(cur, 1));
262+
error("Unexpected character " + std::string(cur, 1));
263+
}
264+
} break;
265+
case IN_BEGIN_ATTRIBUTES: {
266+
pushState(IN_BEGIN_KEY);
267+
} break;
268+
case IN_BEGIN_KEY: {
269+
pushState(IN_END_KEY);
270+
token(IN_STRING, "=");
271+
} break;
272+
case IN_END_KEY: {
273+
if (*next == '=') {
274+
assignLastStringMatch("key", 1000, currentKey, IN_BEGIN_VALUE);
275+
} else {
276+
error("missing value for attribute key");
222277
}
223278
} break;
279+
case IN_BEGIN_VALUE: {
280+
pushState(IN_END_VALUE);
281+
token(IN_STRING, "&;");
282+
} break;
283+
case IN_END_VALUE: {
284+
if (*next == '&') {
285+
assignLastStringMatch("value", 1000, currentValue, IN_END_VALUE);
286+
if (*currentKey == "lifetime" && currentValue == "condition") {
287+
currentLifetime = Lifetime::Condition;
288+
}
289+
attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}});
290+
}
291+
if (*next == ';') {
292+
assignLastStringMatch("value", 1000, currentValue, IN_END_ATTRIBUTES);
293+
if (*currentKey == "lifetime" && currentValue == "condition") {
294+
currentLifetime = Lifetime::Condition;
295+
}
296+
attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}});
297+
}
298+
if (*next == '\0') {
299+
assignLastStringMatch("value", 1000, currentValue, IN_END_ATTRIBUTES);
300+
if (*currentKey == "lifetime" && currentValue == "condition") {
301+
currentLifetime = Lifetime::Condition;
302+
}
303+
attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}});
304+
} else {
305+
error("missing value for string value");
306+
}
307+
} break;
308+
case IN_END_ATTRIBUTES: {
309+
pushState(IN_END_MATCHER);
310+
} break;
224311
case IN_ERROR: {
225312
throw std::runtime_error("Parse error: " + errorString);
226313
} break;
@@ -336,5 +423,4 @@ std::smatch DataDescriptorQueryBuilder::getTokens(std::string const& nodeString)
336423
return m;
337424
}
338425

339-
} // namespace framework
340-
} // namespace o2
426+
} // namespace o2::framework

Framework/Core/test/test_DataDescriptorMatcher.cxx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,15 @@ BOOST_AUTO_TEST_CASE(DataQuery)
537537
BOOST_CHECK_EQUAL(ex.what(), "Parse error: Remove trailing ;");
538538
return true;
539539
};
540+
541+
auto missing_value = [](std::runtime_error const& ex) -> bool {
542+
BOOST_CHECK_EQUAL(ex.what(), "Parse error: value needs to be between 1 and 1000 char long");
543+
return true;
544+
};
545+
auto missing_key = [](std::runtime_error const& ex) -> bool {
546+
BOOST_CHECK_EQUAL(ex.what(), "Parse error: missing value for attribute key");
547+
return true;
548+
};
540549
// Empty query.
541550
BOOST_CHECK(DataDescriptorQueryBuilder::parse().empty() == true);
542551
// Empty bindings.
@@ -606,4 +615,11 @@ BOOST_AUTO_TEST_CASE(DataQuery)
606615
<< DataDescriptorMatcher::Op::Xor
607616
<< DataDescriptorMatcher::Op::Just;
608617
BOOST_CHECK_EQUAL(ops.str(), "andorxorjust");
618+
619+
// Let's check the metadata associated to a query
620+
auto result2 = DataDescriptorQueryBuilder::parse("x:TST/A1/0?lifetime=condition");
621+
BOOST_CHECK(result2[0].lifetime == Lifetime::Condition);
622+
623+
BOOST_CHECK_EXCEPTION(DataDescriptorQueryBuilder::parse("x:TST/A1/0?lifetime="), std::runtime_error, missing_value);
624+
BOOST_CHECK_EXCEPTION(DataDescriptorQueryBuilder::parse("x:TST/A1/0?"), std::runtime_error, missing_key);
609625
}

0 commit comments

Comments
 (0)