1313#include " Framework/WorkflowSpec.h"
1414#include " Framework/DataDescriptorQueryBuilder.h"
1515#include " Framework/DataSpecUtils.h"
16+ #include " Framework/VariantHelpers.h"
1617#include " Framework/VariantJSONHelpers.h"
1718#include " Framework/DataDescriptorMatcher.h"
19+ #include " Framework/DataMatcherWalker.h"
1820#include " Framework/Logger.h"
1921
2022#include < rapidjson/reader.h>
@@ -56,6 +58,7 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
5658 IN_INPUT_DESCRIPTION,
5759 IN_INPUT_SUBSPEC,
5860 IN_INPUT_LIFETIME,
61+ IN_INPUT_STARTTIME,
5962 IN_INPUT_MATCHER,
6063 IN_INPUT_MATCHER_OPERATION,
6164 IN_INPUT_LEFT_MATCHER,
@@ -166,6 +169,9 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
166169 case State::IN_INPUT_LIFETIME:
167170 s << " IN_INPUT_LIFETIME" ;
168171 break ;
172+ case State::IN_INPUT_STARTTIME:
173+ s << " IN_INPUT_STARTTIME" ;
174+ break ;
169175 case State::IN_INPUT_OPTIONS:
170176 s << " IN_INPUT_OPTIONS" ;
171177 break ;
@@ -267,11 +273,13 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
267273 push (State::IN_INPUT);
268274 inputMatcherNodes.clear ();
269275 } else if (in (State::IN_INPUT_MATCHER)) {
270- assert ( 0 ); // to be implemented
276+ // start a new embedded matcher
271277 } else if (in (State::IN_INPUT_LEFT_MATCHER)) {
272- assert (0 ); // to be implemented
278+ // this is a matcher leaf, i.e. last matcher of a branch
279+ // will be merged into the parent matcher
273280 } else if (in (State::IN_INPUT_RIGHT_MATCHER)) {
274- assert (0 ); // to be implemented
281+ // this is a matcher leaf, i.e. last matcher of a branch
282+ // will be merged into the parent matcher
275283 } else if (in (State::IN_OUTPUTS)) {
276284 push (State::IN_OUTPUT);
277285 outputHasSubSpec = false ;
@@ -313,7 +321,13 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
313321 return lastMatcher;
314322 };
315323
316- auto matcher = buildMatcher (inputMatcherNodes);
324+ std::unique_ptr<DataDescriptorMatcher> matcher;
325+ if (auto * pval = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&inputMatcherNodes[0 ])) {
326+ assert (inputMatcherNodes.size () == 1 );
327+ matcher = std::move (*pval);
328+ } else {
329+ matcher = buildMatcher (inputMatcherNodes);
330+ }
317331 auto concrete = DataSpecUtils::optionalConcreteDataMatcherFrom (*matcher);
318332 if (concrete.has_value ()) {
319333 // the matcher is fully qualified with unique parameters so we add ConcreteDataMatcher
@@ -324,6 +338,60 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
324338 inputMatcherNodes.clear ();
325339 inputOptions.clear ();
326340
341+ } else if (in (State::IN_INPUT_MATCHER) && inputMatcherNodes.size () > 1 ) {
342+ data_matcher::Node child = std::move (inputMatcherNodes.back ());
343+ inputMatcherNodes.pop_back ();
344+ auto * matcher = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&child);
345+ assert (matcher != nullptr );
346+ auto * parent = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&inputMatcherNodes.back ());
347+ assert (parent != nullptr );
348+ std::unique_ptr<DataDescriptorMatcher> node;
349+ auto mergeDown = [&node, &parent, &child]() -> bool {
350+ // FIXME: do we need a dedicated default state, or can we simply use ConstantValueMatcher
351+ if (auto * pval1 = std::get_if<ConstantValueMatcher>(&((*parent)->getLeft ()))) {
352+ if (*pval1 == ConstantValueMatcher{false }) {
353+ node = std::make_unique<DataDescriptorMatcher>((*parent)->getOp (),
354+ std::move (child),
355+ std::move ((*parent)->getRight ()));
356+ return true ;
357+ }
358+ }
359+ if (auto * pval2 = std::get_if<ConstantValueMatcher>(&((*parent)->getRight ()))) {
360+ if (*pval2 == ConstantValueMatcher{false }) {
361+ node = std::make_unique<DataDescriptorMatcher>((*parent)->getOp (),
362+ std::move ((*parent)->getLeft ()),
363+ std::move (child));
364+ return true ;
365+ }
366+ }
367+ return false ;
368+ };
369+ if (!mergeDown ()) {
370+ states.push_back (State::IN_ERROR);
371+ }
372+ inputMatcherNodes.pop_back ();
373+ inputMatcherNodes.push_back (std::move (node));
374+ } else if (in (State::IN_INPUT_LEFT_MATCHER)) {
375+ assert (inputMatcherNodes.size () >= 2 );
376+ size_t nMatchers = inputMatcherNodes.size ();
377+ auto * parent = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&inputMatcherNodes[nMatchers - 2 ]);
378+ assert (parent != nullptr );
379+ auto node = std::make_unique<DataDescriptorMatcher>((*parent)->getOp (),
380+ std::move (inputMatcherNodes[nMatchers - 1 ]),
381+ std::move ((*parent)->getRight ()));
382+ inputMatcherNodes.pop_back ();
383+ inputMatcherNodes.pop_back ();
384+ inputMatcherNodes.push_back (std::move (node));
385+ } else if (in (State::IN_INPUT_RIGHT_MATCHER)) {
386+ data_matcher::Node child = std::move (inputMatcherNodes.back ());
387+ inputMatcherNodes.pop_back ();
388+ auto * parent = std::get_if<std::unique_ptr<DataDescriptorMatcher>>(&inputMatcherNodes.back ());
389+ assert (parent != nullptr );
390+ auto node = std::make_unique<DataDescriptorMatcher>((*parent)->getOp (),
391+ std::move ((*parent)->getLeft ()),
392+ std::move (child));
393+ inputMatcherNodes.pop_back ();
394+ inputMatcherNodes.push_back (std::move (node));
327395 } else if (in (State::IN_OUTPUT)) {
328396 if (outputHasSubSpec) {
329397 dataProcessors.back ().outputs .push_back (OutputSpec ({binding}, origin, description, subspec, lifetime));
@@ -479,13 +547,13 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
479547 } else if (in (State::IN_INPUT) && strncmp (str, " subspec" , length) == 0 ) {
480548 push (State::IN_INPUT_SUBSPEC);
481549 } else if (in (State::IN_INPUT) && strncmp (str, " matcher" , length) == 0 ) {
482- assert (0 );
483550 // the outermost matcher is starting here
484551 // we create a placeholder which is being updated later
485552 inputMatcherNodes.push_back (std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::And, ConstantValueMatcher{false }));
486553 push (State::IN_INPUT_MATCHER);
487554 } else if (in (State::IN_INPUT_MATCHER) && strncmp (str, " matcher" , length) == 0 ) {
488- // recursive matchers, can maybe combine with above
555+ // recursive matchers
556+ inputMatcherNodes.push_back (std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::And, ConstantValueMatcher{false }));
489557 push (State::IN_INPUT_MATCHER);
490558 } else if (in (State::IN_INPUT_MATCHER) && strncmp (str, " operation" , length) == 0 ) {
491559 push (State::IN_INPUT_MATCHER_OPERATION);
@@ -505,8 +573,14 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
505573 push (State::IN_INPUT_SUBSPEC);
506574 } else if (in (State::IN_INPUT_RIGHT_MATCHER) && strncmp (str, " subspec" , length) == 0 ) {
507575 push (State::IN_INPUT_SUBSPEC);
576+ } else if (in (State::IN_INPUT_LEFT_MATCHER) && strncmp (str, " starttime" , length) == 0 ) {
577+ push (State::IN_INPUT_STARTTIME);
578+ } else if (in (State::IN_INPUT_RIGHT_MATCHER) && strncmp (str, " starttime" , length) == 0 ) {
579+ push (State::IN_INPUT_STARTTIME);
508580 } else if (in (State::IN_INPUT) && strncmp (str, " lifetime" , length) == 0 ) {
509581 push (State::IN_INPUT_LIFETIME);
582+ } else if (in (State::IN_INPUT) && strncmp (str, " starttime" , length) == 0 ) {
583+ push (State::IN_INPUT_STARTTIME);
510584 } else if (in (State::IN_INPUT) && strncmp (str, " metadata" , length) == 0 ) {
511585 push (State::IN_INPUT_OPTIONS);
512586 } else if (in (State::IN_OUTPUT) && strncmp (str, " binding" , length) == 0 ) {
@@ -594,8 +668,30 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
594668 description.runtimeInit (s.c_str (), std::min (s.size (), 16UL ));
595669 std::string v (s.c_str (), std::min (s.size (), 16UL ));
596670 inputMatcherNodes.push_back (DescriptionValueMatcher{v});
671+ } else if (in (State::IN_INPUT_STARTTIME)) {
672+ // we add StartTimeValueMatcher with ContextRef for starttime, no matter what
673+ // has been in the configuration.
674+ inputMatcherNodes.push_back (StartTimeValueMatcher (ContextRef{ContextPos::STARTTIME_POS}));
597675 } else if (in (State::IN_INPUT_MATCHER_OPERATION)) {
598- // FIXME: read operation
676+ // FIXME: need to implement operator>> to read the op parameter
677+ DataDescriptorMatcher::Op op = DataDescriptorMatcher::Op::And;
678+ if (s == " and" ) {
679+ op = DataDescriptorMatcher::Op::And;
680+ } else if (s == " or" ) {
681+ op = DataDescriptorMatcher::Op::Or;
682+ } else if (s == " xor" ) {
683+ op = DataDescriptorMatcher::Op::Xor;
684+ } else if (s == " just" ) {
685+ op = DataDescriptorMatcher::Op::Just;
686+ } else if (s == " not" ) {
687+ op = DataDescriptorMatcher::Op::Not;
688+ }
689+ // FIXME: we could drop the placeholder which has been added when entering
690+ // the states which can read key 'operation', but then we need to make sure
691+ // that this key is always present
692+ auto node = std::make_unique<DataDescriptorMatcher>(op, ConstantValueMatcher{false });
693+ inputMatcherNodes.pop_back ();
694+ inputMatcherNodes.push_back (std::move (node));
599695 } else if (in (State::IN_OUTPUT_BINDING)) {
600696 binding = s;
601697 } else if (in (State::IN_OUTPUT_ORIGIN)) {
@@ -629,6 +725,10 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>,
629725 push (State::IN_METADATUM_CHANNEL);
630726 } else if (in (State::IN_COMMAND)) {
631727 command.merge ({s});
728+ } else {
729+ std::stringstream errstr;
730+ errstr << " No string handling for argument '" << std::string (str, length) << " ' in state " << states.back () << std::endl;
731+ throw std::runtime_error (errstr.str ());
632732 }
633733 pop ();
634734 return true ;
@@ -788,6 +888,67 @@ void WorkflowSerializationHelpers::dump(std::ostream& out,
788888 rapidjson::OStreamWrapper osw (out);
789889 rapidjson::PrettyWriter<rapidjson::OStreamWrapper> w (osw);
790890
891+ // handlers for serialization of InputSpec matchers
892+ auto edgeWalker = overloaded{
893+ [&w](EdgeActions::EnterNode action) {
894+ w.Key (" matcher" );
895+ w.StartObject ();
896+ w.Key (" operation" );
897+ std::stringstream ss;
898+ ss << action.node ->getOp ();
899+ w.String (ss.str ().c_str ());
900+ if (action.node ->getOp () == DataDescriptorMatcher::Op::Just ||
901+ action.node ->getOp () == DataDescriptorMatcher::Op::Not) {
902+ return ChildAction::VisitLeft;
903+ }
904+ return ChildAction::VisitBoth;
905+ },
906+ [&w](EdgeActions::EnterLeft) {
907+ w.Key (" left" );
908+ w.StartObject ();
909+ },
910+ [&w](EdgeActions::ExitLeft) {
911+ w.EndObject ();
912+ },
913+ [&w](EdgeActions::EnterRight) {
914+ w.Key (" right" );
915+ w.StartObject ();
916+ },
917+ [&w](EdgeActions::ExitRight) {
918+ w.EndObject ();
919+ },
920+ [&w](EdgeActions::ExitNode) {
921+ w.EndObject ();
922+ },
923+ [&w](auto ) {}};
924+ auto leafWalker = overloaded{
925+ [&w](OriginValueMatcher const & origin) {
926+ w.Key (" origin" );
927+ std::stringstream ss;
928+ ss << origin;
929+ w.String (ss.str ().c_str ());
930+ },
931+ [&w](DescriptionValueMatcher const & description) {
932+ w.Key (" description" );
933+ std::stringstream ss;
934+ ss << description;
935+ w.String (ss.str ().c_str ());
936+ },
937+ [&w](SubSpecificationTypeValueMatcher const & subspec) {
938+ w.Key (" subspec" );
939+ std::stringstream ss;
940+ ss << subspec;
941+ w.Uint64 (std::stoul (ss.str ()));
942+ },
943+ [&w](StartTimeValueMatcher const & startTime) {
944+ w.Key (" starttime" );
945+ std::stringstream ss;
946+ ss << startTime;
947+ w.String (ss.str ().c_str ());
948+ },
949+ [&w](ConstantValueMatcher const & constant) {},
950+ [&w](auto t) {}};
951+
791952 w.StartObject ();
792953 w.Key (" workflow" );
793954 w.StartArray ();
@@ -802,27 +963,28 @@ void WorkflowSerializationHelpers::dump(std::ostream& out,
802963
803964 w.Key (" inputs" );
804965 w.StartArray ();
805- for (auto & input : processor.inputs ) {
966+ for (auto const & input : processor.inputs ) {
806967 // / FIXME: this only works for a selected set of InputSpecs...
807968 // / a proper way to fully serialize an InputSpec with
808969 // / a DataDescriptorMatcher is needed.
809970 w.StartObject ();
810971 w.Key (" binding" );
811972 w.String (input.binding .c_str ());
812- auto origin = DataSpecUtils::getOptionalOrigin (input);
813- if (origin.has_value ()) {
973+ if (auto const * concrete = std::get_if<ConcreteDataMatcher>(&input.matcher )) {
814974 w.Key (" origin" );
815- w.String (origin->str , strnlen (origin->str , 4 ));
816- }
817- auto description = DataSpecUtils::getOptionalDescription (input);
818- if (description.has_value ()) {
975+ w.String (concrete->origin .str , strnlen (concrete->origin .str , 4 ));
819976 w.Key (" description" );
820- w.String (description->str , strnlen (description->str , 16 ));
821- }
822- auto subSpec = DataSpecUtils::getOptionalSubSpec (input);
823- if (subSpec.has_value ()) {
977+ w.String (concrete->description .str , strnlen (concrete->description .str , 16 ));
824978 w.Key (" subspec" );
825- w.Uint64 (*subSpec);
979+ w.Uint64 (concrete->subSpec );
980+ // auto tmp = DataSpecUtils::dataDescriptorMatcherFrom(*concrete);
981+ // DataMatcherWalker::walk(tmp,
982+ // edgeWalker,
983+ // leafWalker);
984+ } else if (auto const * matcher = std::get_if<DataDescriptorMatcher>(&input.matcher )) {
985+ DataMatcherWalker::walk (*matcher,
986+ edgeWalker,
987+ leafWalker);
826988 }
827989 w.Key (" lifetime" );
828990 w.Uint ((int )input.lifetime );
0 commit comments