Project
Loading...
Searching...
No Matches
DataDescriptorQueryBuilder.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14#include "Framework/InputSpec.h"
15
16#include <memory>
17#include <optional>
18#include <string>
19#include <vector>
20#include <regex>
21#include <iostream>
22
23using namespace o2::framework::data_matcher;
24
25namespace o2::framework
26{
27
54
55std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
56{
57 std::vector<QueryBuilderState> states;
58 states.push_back(IN_BEGIN_QUERY);
59
60 std::vector<InputSpec> result;
61 char const* next = config;
62 char const* cur = config;
63 char const* expectedSeparators;
64 char* endptr;
65 std::string errorString;
66 std::unique_ptr<DataDescriptorMatcher> currentMatcher;
67 std::vector<data_matcher::Node> nodes;
68 std::optional<std::string> currentBinding;
69 std::optional<std::string> currentOrigin;
70 std::optional<std::string> currentDescription;
71 std::optional<std::string> currentKey;
72 std::optional<std::string> currentValue;
73 std::optional<header::DataHeader::SubSpecificationType> currentSubSpec;
74 std::optional<uint64_t> currentTimeModulo;
75 Lifetime currentLifetime = Lifetime::Timeframe;
76 size_t currentNumber;
77
78 auto error = [&errorString, &states](std::string const& s) {
79 errorString = s;
80 states.push_back(IN_ERROR);
81 };
82
83 auto pushState = [&states](QueryBuilderState state) {
84 states.push_back(state);
85 };
86
87 auto checkModifier = [&states, &cur, &next, &error](QueryBuilderState state, const char* mod) {
88 const char* modifier = nullptr;
89 if (mod[0] != '\0' && (modifier = strpbrk(cur, mod)) != nullptr) {
90 switch (*modifier) {
91 case '!':
92 states.push_back(IN_NEGATION);
93 break;
94 default:
95 error("invalid modifier '" + std::string(modifier, 1) + "'");
96 return;
97 }
98 next = ++cur;
99 }
100 states.push_back(state);
101 };
102
103 auto token = [&states, &expectedSeparators](QueryBuilderState state, char const* sep) {
104 states.push_back(state);
105 expectedSeparators = sep;
106 };
107
108 auto assignLastStringMatch = [&next, &cur, &error, &pushState](std::string const& what, int maxSize, std::optional<std::string>& s, QueryBuilderState nextState) {
109 if ((next - cur == 0) || (next - cur > maxSize)) {
110 error(what + " needs to be between 1 and " + std::to_string(maxSize) + " char long");
111 return false;
112 }
113 s = std::string(cur, next - cur);
114 if (memchr("\0;", *next, 2)) {
115 pushState(IN_END_MATCHER);
116 cur = next;
117 return true;
118 }
119 pushState(nextState);
120 cur = next + 1;
121 return true;
122 };
123
124 auto assignLastNumericMatch = [&next, &cur, &error, &pushState, &currentNumber](std::string const&, auto& value,
125 QueryBuilderState nextState) {
126 if ((next - cur == 0)) {
127 error("number expected");
128 return false;
129 }
130 value = currentNumber;
131 if (memchr("\0;", *next, 2)) {
132 pushState(IN_END_MATCHER);
133 cur = next;
134 return true;
135 }
136 pushState(nextState);
137 cur = next + 1;
138 return true;
139 };
140
141 std::vector<ConfigParamSpec> attributes;
142
143 auto buildMatchingTree = [&nodes](std::string const& binding, std::vector<ConfigParamSpec> attributes) -> InputSpec {
144 auto lastMatcher =
145 std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Just,
147 for (size_t ni = 0, ne = nodes.size(); ni < ne; ++ni) {
148 auto& node = nodes[nodes.size() - 1 - ni];
149 auto tmp = std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::And,
150 std::move(node),
151 std::move(lastMatcher));
152 assert(lastMatcher.get() == nullptr);
153 lastMatcher = std::move(tmp);
154 }
155 Lifetime lifetime = Lifetime::Timeframe;
156 for (auto& attribute : attributes) {
157 if (attribute.name == "lifetime" && attribute.defaultValue.get<std::string>() == "condition") {
158 lifetime = Lifetime::Condition;
159 }
160 if (attribute.name == "lifetime" && attribute.defaultValue.get<std::string>() == "sporadic") {
161 lifetime = Lifetime::Sporadic;
162 }
163 }
164 return InputSpec{binding, std::move(*lastMatcher), lifetime, attributes};
165 };
166
167 auto pushMatcher = [&nodes, &states](auto&& matcher) {
168 if (states.empty() == false && states.back() == IN_NEGATION) {
169 states.pop_back();
170 auto notMatcher = std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Xor,
171 std::move(matcher),
173 nodes.push_back(std::move(notMatcher));
174 } else {
175 nodes.push_back(std::move(matcher));
176 }
177 };
178
179 while (states.empty() == false) {
180 auto const state = states.back();
181 states.pop_back();
182
183 switch (state) {
184 case IN_STRING: {
185 next = strpbrk(cur, expectedSeparators);
186 if (next == nullptr) {
187 next = cur + strlen(cur);
188 }
189 } break;
190 case IN_NUMBER: {
191 currentNumber = strtoll(cur, &endptr, 0);
192 if (endptr == cur) {
193 error("Expected a number");
194 }
195 next = endptr;
196 } break;
197 case IN_BEGIN_QUERY: {
198 (*cur == 0) ? pushState(IN_END_QUERY)
199 : pushState(IN_BEGIN_MATCHER);
200 } break;
201 case IN_BEGIN_MATCHER: {
202 nodes.clear();
203 attributes.clear();
204 // Reset per-query state, so a ;-separated query cannot inherit it.
205 currentBinding.reset();
206 currentOrigin.reset();
207 currentDescription.reset();
208 currentSubSpec.reset();
209 currentTimeModulo.reset();
210 pushState(IN_BEGIN_BINDING);
211 } break;
212 case IN_BEGIN_BINDING: {
213 pushState(IN_END_BINDING);
214 token(IN_STRING, ":/;,");
215 } break;
216 case IN_END_BINDING: {
217 // We are at the end of the string already.
218 // This is really an origin...
219 if (memchr("\0/;", *next, 3)) {
220 pushState(IN_END_ORIGIN);
221 continue;
222 }
223 if (next - cur == 0) {
224 error("empty binding string");
225 continue;
226 }
227 currentBinding = std::string(cur, next - cur);
228 pushState(IN_BEGIN_ORIGIN);
229 cur = next + 1;
230 } break;
231 case IN_BEGIN_ORIGIN: {
232 pushState(IN_END_ORIGIN);
233 token(IN_STRING, "/;?");
234 } break;
235 case IN_END_ORIGIN: {
236 if (*next == '/' && assignLastStringMatch("origin", 4, currentOrigin, IN_BEGIN_DESCRIPTION)) {
237 nodes.push_back(OriginValueMatcher{*currentOrigin});
238 } else if (*next == ';' && assignLastStringMatch("origin", 4, currentOrigin, IN_END_MATCHER)) {
239 nodes.push_back(OriginValueMatcher{*currentOrigin});
240 } else if (*next == '?' && assignLastStringMatch("origin", 4, currentOrigin, IN_BEGIN_ATTRIBUTES)) {
241 nodes.push_back(OriginValueMatcher{*currentOrigin});
242 } else if (*next == '\0' && assignLastStringMatch("origin", 4, currentOrigin, IN_END_MATCHER)) {
243 nodes.push_back(OriginValueMatcher{*currentOrigin});
244 } else {
245 error("origin needs to be between 1 and 4 char long");
246 }
247 } break;
249 pushState(IN_END_DESCRIPTION);
250 token(IN_STRING, "/;?");
251 } break;
252 case IN_END_DESCRIPTION: {
253 if (*next == '/' && assignLastStringMatch("description", 16, currentDescription, IN_BEGIN_SUBSPEC)) {
254 nodes.push_back(DescriptionValueMatcher{*currentDescription});
255 } else if (*next == ';' && assignLastStringMatch("description", 16, currentDescription, IN_END_MATCHER)) {
256 nodes.push_back(DescriptionValueMatcher{*currentDescription});
257 } else if (*next == '?' && assignLastStringMatch("description", 16, currentDescription, IN_BEGIN_ATTRIBUTES)) {
258 nodes.push_back(DescriptionValueMatcher{*currentDescription});
259 } else if (*next == '\0' && assignLastStringMatch("description", 16, currentDescription, IN_END_MATCHER)) {
260 nodes.push_back(DescriptionValueMatcher{*currentDescription});
261 } else {
262 error("description needs to be between 1 and 16 char long");
263 }
264 } break;
265 case IN_BEGIN_SUBSPEC: {
266 checkModifier(IN_END_SUBSPEC, "!");
267 token(IN_NUMBER, ";%?");
268 } break;
269 case IN_END_SUBSPEC: {
270 if (*next == '%' && assignLastNumericMatch("subspec", currentSubSpec, IN_BEGIN_TIMEMODULO)) {
271 } else if (*next == '?' && assignLastNumericMatch("subspec", currentSubSpec, IN_BEGIN_ATTRIBUTES)) {
272 } else if (*next == ';' && assignLastNumericMatch("subspec", currentSubSpec, IN_END_MATCHER)) {
273 } else if (*next == '\0' && assignLastNumericMatch("subspec", currentSubSpec, IN_END_MATCHER)) {
274 } else {
275 error("Expected a number");
276 break;
277 }
278 auto backup = states.back();
279 states.pop_back();
280 pushMatcher(SubSpecificationTypeValueMatcher{*currentSubSpec});
281 states.push_back(backup);
282 } break;
283 case IN_BEGIN_TIMEMODULO: {
284 pushState(IN_END_TIMEMODULO);
285 token(IN_NUMBER, ";?");
286 } break;
287 case IN_END_TIMEMODULO: {
288 assignLastNumericMatch("timemodulo", currentTimeModulo, IN_ERROR);
289 } break;
290 case IN_END_MATCHER: {
291 if (*cur == ';' && *(cur + 1) == '\0') {
292 error("Remove trailing ;");
293 continue;
294 }
295 // A query without a binding, e.g. "TST/A1", is named after its origin.
296 result.push_back(buildMatchingTree(currentBinding.value_or(currentOrigin.value_or("")), attributes));
297 if (*cur == '\0') {
298 pushState(IN_END_QUERY);
299 } else if (*cur == ';') {
300 cur += 1;
301 pushState(IN_BEGIN_MATCHER);
302 } else {
303 error("Unexpected character " + std::string(cur, 1));
304 }
305 } break;
306 case IN_BEGIN_ATTRIBUTES: {
307 pushState(IN_BEGIN_KEY);
308 } break;
309 case IN_BEGIN_KEY: {
310 pushState(IN_END_KEY);
311 token(IN_STRING, "=");
312 } break;
313 case IN_END_KEY: {
314 if (*next == '=') {
315 assignLastStringMatch("key", 1000, currentKey, IN_BEGIN_VALUE);
316 } else {
317 error("missing value for attribute key");
318 }
319 } break;
320 case IN_BEGIN_VALUE: {
321 pushState(IN_END_VALUE);
322 token(IN_STRING, "&;");
323 } break;
324 case IN_END_VALUE: {
325 if (*next == '&') {
326 assignLastStringMatch("value", 1000, currentValue, IN_BEGIN_KEY);
327 if (*currentKey == "lifetime" && currentValue == "condition") {
328 currentLifetime = Lifetime::Condition;
329 }
330 if (*currentKey == "ccdb-run-dependent") {
331 int val = currentValue == "false" ? 0 : (currentValue == "true" ? 1 : std::stoi(*currentValue));
332 attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Int, val, {}});
333 } else {
334 attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}});
335 }
336 } else if (*next == ';') {
337 assignLastStringMatch("value", 1000, currentValue, IN_END_ATTRIBUTES);
338 if (*currentKey == "lifetime" && currentValue == "condition") {
339 currentLifetime = Lifetime::Condition;
340 }
341 if (*currentKey == "ccdb-run-dependent") {
342 int val = currentValue == "false" ? 0 : (currentValue == "true" ? 1 : std::stoi(*currentValue));
343 attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Int, val, {}});
344 } else {
345 attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}});
346 }
347 } else if (*next == '\0') {
348 assignLastStringMatch("value", 1000, currentValue, IN_END_ATTRIBUTES);
349 if (*currentKey == "lifetime" && currentValue == "condition") {
350 currentLifetime = Lifetime::Condition;
351 }
352 if (*currentKey == "ccdb-run-dependent") {
353 int val = currentValue == "false" ? 0 : (currentValue == "true" ? 1 : std::stoi(*currentValue));
354 attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Int, val, {}});
355 } else {
356 attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}});
357 }
358 } else {
359 error("missing value for string value");
360 }
361 } break;
362 case IN_END_ATTRIBUTES: {
363 pushState(IN_END_MATCHER);
364 } break;
365 case IN_NEGATION: {
366 error("property modifiers should have been handled before when inserting previous matcher");
367 } break;
368 case IN_ERROR: {
369 throw std::runtime_error("Parse error: " + errorString);
370 } break;
371 case IN_END_QUERY: {
372 } break;
373 default: {
374 error("Unhandled state");
375 } break;
376 }
377 }
378 return std::move(result);
379}
380
382{
383 static const std::regex delim(",");
384
385 std::sregex_token_iterator end;
386 std::sregex_token_iterator iter(config.begin(),
387 config.end(),
388 delim,
389 -1);
390
391 std::unique_ptr<DataDescriptorMatcher> next, result;
392
393 for (; iter != end; ++iter) {
394 std::smatch m;
395 auto s = iter->str();
396 auto newNode = buildNode(s);
397
398 if (result.get() == nullptr) {
399 result = std::move(newNode);
400 } else {
401 next = std::move(std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Or,
402 std::move(result),
403 std::move(newNode)));
404 result = std::move(next);
405 }
406 }
407
408 return std::move(DataDescriptorQuery{{}, std::move(result)});
409}
410
412{
413 static const std::regex delim1(",");
414 static const std::regex delim2(":");
415
416 std::sregex_token_iterator end;
417 std::sregex_token_iterator iter1(config.begin(),
418 config.end(),
419 delim1,
420 -1);
421
422 std::unique_ptr<DataDescriptorMatcher> next, result;
423
424 // looping over ','-separated items
425 for (; iter1 != end; ++iter1) {
426 auto s = iter1->str();
427
428 // get first part of item
429 std::sregex_token_iterator iter2(s.begin(),
430 s.end(),
431 delim2,
432 -1);
433 if (iter2 == end) {
434 continue;
435 }
436 s = iter2->str();
437
438 // create the corresponding DataDescriptorMatcher
439 std::smatch m;
440 auto newNode = buildNode(s);
441
442 if (result.get() == nullptr) {
443 result = std::move(newNode);
444 } else {
445 next = std::move(std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Or,
446 std::move(result),
447 std::move(newNode)));
448 result = std::move(next);
449 }
450 }
451
452 return std::move(DataDescriptorQuery{{}, std::move(result)});
453}
454
455std::unique_ptr<DataDescriptorMatcher> DataDescriptorQueryBuilder::buildNode(std::string const& nodeString)
456{
457
458 auto m = getTokens(nodeString);
459
460 std::unique_ptr<DataDescriptorMatcher> next;
461 auto newNode = std::make_unique<DataDescriptorMatcher>(
462 DataDescriptorMatcher::Op::And,
464 std::make_unique<DataDescriptorMatcher>(
465 DataDescriptorMatcher::Op::And,
468
469 return newNode;
470}
471
472std::vector<std::string> DataDescriptorQueryBuilder::getTokens(std::string const& nodeString)
473{
474
475 static const std::regex specTokenRE(R"re((\w{1,4})/(\w{1,16})/(\d*))re");
476 std::smatch match;
477
478 std::regex_match(nodeString, match, specTokenRE);
479
480 std::vector<std::string> result;
481 for (size_t i = 0; i < 4; ++i) {
482 result.push_back(match[i].str());
483 }
484 return result;
485}
486
487} // namespace o2::framework
std::string binding
size_t maxSize
std::string currentKey
std::unique_ptr< expressions::Node > node
int32_t i
SurfaceTrackState state
Something which can be matched against a header::DataDescription.
Something which can be matched against a header::DataOrigin.
Matcher on actual time, as reported in the DataProcessingHeader.
Something which can be matched against a header::SubSpecificationType.
bool match(const std::vector< std::string > &queries, const char *pattern)
Definition dcs-ccdb.cxx:229
const GLfloat * m
Definition glcorearb.h:4066
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint GLuint end
Definition glcorearb.h:469
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLuint GLfloat * val
Definition glcorearb.h:1582
GLuint * states
Definition glcorearb.h:4932
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
Lifetime
Possible Lifetime of objects being exchanged by the DPL.
Definition Lifetime.h:18
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
static std::vector< InputSpec > parse(const char *s="")
static std::unique_ptr< data_matcher::DataDescriptorMatcher > buildNode(std::string const &nodeString)
static DataDescriptorQuery buildFromExtendedKeepConfig(std::string const &config)
deprecated?
static std::vector< std::string > getTokens(std::string const &nodeString)
static DataDescriptorQuery buildFromKeepConfig(std::string const &config)
A typesafe reference to an element of the context.
const std::string str