1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "HRSchemaSerializer.h"
23 
24 #include <QTextStream>
25 
26 #include <U2Core/AppContext.h>
27 #include <U2Core/BaseDocumentFormats.h>
28 #include <U2Core/Counter.h>
29 #include <U2Core/GUrl.h>
30 #include <U2Core/L10n.h>
31 #include <U2Core/Log.h>
32 #include <U2Core/U2OpStatusUtils.h>
33 #include <U2Core/U2SafePoints.h>
34 
35 #include <U2Lang/ActorModel.h>
36 #include <U2Lang/ActorPrototypeRegistry.h>
37 #include <U2Lang/BaseActorCategories.h>
38 #include <U2Lang/BaseAttributes.h>
39 #include <U2Lang/BaseTypes.h>
40 #include <U2Lang/CoreLibConstants.h>
41 #include <U2Lang/Dataset.h>
42 #include <U2Lang/ExternalToolCfg.h>
43 #include <U2Lang/GrouperSlotAttribute.h>
44 #include <U2Lang/HRWizardSerializer.h>
45 #include <U2Lang/IncludedProtoFactory.h>
46 #include <U2Lang/IntegralBusModel.h>
47 #include <U2Lang/Marker.h>
48 #include <U2Lang/MarkerAttribute.h>
49 #include <U2Lang/SchemaSerializer.h>
50 #include <U2Lang/ScriptWorkerSerializer.h>
51 #include <U2Lang/SharedDbUrlUtils.h>
52 #include <U2Lang/WorkflowEnv.h>
53 #include <U2Lang/WorkflowSettings.h>
54 #include <U2Lang/WorkflowUtils.h>
55 
56 #include "Constants.h"
57 #include "HRVisualSerializer.h"
58 #include "OldUWL.h"
59 
60 namespace U2 {
61 using namespace WorkflowSerialize;
62 
63 namespace {
64 template<class T>
setIfNotNull(const T & what,T * to)65 void setIfNotNull(const T &what, T *to) {
66     if (to != nullptr) {
67         *to = what;
68     }
69 }
70 
getAttribute(Actor * proc,const QString & attrId)71 Attribute *getAttribute(Actor *proc, const QString &attrId) {
72     assert(proc != nullptr);
73     if (proc->hasParameter(attrId)) {
74         return proc->getParameter(attrId);
75     } else if (proc->hasParameter(BaseAttributes::URL_IN_ATTRIBUTE().getId()) && attrId == BaseAttributes::URL_LOCATION_ATTRIBUTE().getId()) {
76         Attribute *attr = new Attribute(BaseAttributes::URL_LOCATION_ATTRIBUTE(), BaseTypes::BOOL_TYPE(), false, true);
77         proc->addParameter(BaseAttributes::URL_LOCATION_ATTRIBUTE().getId(), attr);
78         return attr;
79     }
80     return nullptr;
81 }
82 
getAttrValue(Actor * proc,const QString & attrId,const QString & valueStr)83 QVariant getAttrValue(Actor *proc, const QString &attrId, const QString &valueStr) {
84     Attribute *attr = getAttribute(proc, attrId);
85     if (attr == nullptr) {
86         throw ReadFailed(HRSchemaSerializer::tr("Parameter '%1' undefined for element '%2'").arg(attrId).arg(proc->getLabel()));
87     }
88     DataTypeValueFactory *valueFactory = WorkflowEnv::getDataTypeValueFactoryRegistry()->getById(attr->getAttributeType()->getId());
89     if (valueFactory == nullptr) {
90         throw ReadFailed(HRSchemaSerializer::tr("Cannot parse value from '%1': no value factory").arg(valueStr));
91     }
92     bool ok = false;
93     QVariant value = valueFactory->getValueFromString(valueStr, &ok);
94     if (!ok) {
95         throw ReadFailed(HRSchemaSerializer::tr("Cannot parse value from '%1'").arg(valueStr));
96     }
97     return value;
98 }
99 
makeIndent(int tabsNum)100 QString makeIndent(int tabsNum) {
101     tabsNum = tabsNum <= 0 ? 0 : tabsNum;
102     QString res;
103     for (int i = 0; i < tabsNum; ++i) {
104         res += Constants::TAB;
105     }
106     return res;
107 }
108 
quotedString(const QString & str)109 QString quotedString(const QString &str) {
110     return Constants::QUOTE + str + Constants::QUOTE;
111 }
112 
113 enum IncludeElementType {
114     SCHEMA,
115     EXTERNAL_TOOL,
116     SCRIPT
117 };
118 }  // namespace
119 
valueString(const QString & s,bool quoteEmpty)120 QString HRSchemaSerializer::valueString(const QString &s, bool quoteEmpty) {
121     QString str = s;
122     str.replace("\"", "'");
123     if (str.contains(QRegExp("\\s")) || str.contains(Constants::SEMICOLON) ||
124         str.contains(Constants::EQUALS_SIGN) || str.contains(Constants::DATAFLOW_SIGN) ||
125         str.contains(Constants::BLOCK_START) || str.contains(Constants::BLOCK_END) ||
126         str.contains(Constants::SINGLE_QUOTE) || str.contains(OldConstants::MARKER_START) ||
127         (str.isEmpty() && quoteEmpty)) {
128         return quotedString(str);
129     } else {
130         return str;
131     }
132 }
133 
saveSchema(Schema * schema,Metadata * meta,const QString & url,U2OpStatus & os)134 void HRSchemaSerializer::saveSchema(Schema *schema, Metadata *meta, const QString &url, U2OpStatus &os) {
135     QFile file(url);
136     if (!file.open(QIODevice::WriteOnly)) {
137         os.setError(L10N::errorOpeningFileWrite(url));
138         return;
139     }
140     QTextStream out(&file);
141     out.setCodec("UTF-8");
142     out << schema2String(*schema, meta);
143 
144     file.close();
145 }
146 
isHeaderLine(const QString & line)147 bool HRSchemaSerializer::isHeaderLine(const QString &line) {
148     return (line.startsWith(Constants::HEADER_LINE) ||
149             line.startsWith(Constants::DEPRECATED_HEADER_LINE));
150 }
151 
checkHeaderLine(const QString & line,Tokenizer & tokenizer)152 void HRSchemaSerializer::checkHeaderLine(const QString &line, Tokenizer &tokenizer) {
153     if (!isHeaderLine(line)) {
154         if (tokenizer.notEmpty() && line + " " + tokenizer.take() == Constants::OLD_XML_HEADER) {
155             throw ReadFailed(tr("XML workflow format is obsolete and not supported"));
156         }
157         throw ReadFailed(tr("Bad header: expected '%1', got '%2'").arg(Constants::HEADER_LINE).arg(line));
158     }
159 }
160 
parseHeader(Tokenizer & tokenizer,Metadata * meta)161 void HRSchemaSerializer::parseHeader(Tokenizer &tokenizer, Metadata *meta) {
162     QString head = tokenizer.take();
163     checkHeaderLine(head, tokenizer);
164     QString desc;
165     while (tokenizer.look().startsWith(Constants::SERVICE_SYM)) {
166         desc += tokenizer.take().mid(Constants::SERVICE_SYM.size()) + Constants::NEW_LINE;
167     }
168     setIfNotNull<QString>(desc, meta == nullptr ? nullptr : &meta->comment);
169 }
170 
getAbsoluteIncludePath(QString & path)171 static bool getAbsoluteIncludePath(QString &path) {
172     if (QFileInfo(path).isAbsolute()) {
173         return QFile::exists(path);
174     }
175 
176     QString absPath;
177     absPath = WorkflowSettings::getExternalToolDirectory() + path;
178     if (QFile::exists(absPath)) {
179         path = absPath;
180         return true;
181     }
182 
183     absPath = WorkflowSettings::getUserDirectory() + path;
184     if (QFile::exists(absPath)) {
185         path = absPath;
186         return true;
187     }
188 
189     absPath = WorkflowSettings::getIncludedElementsDirectory() + path;
190     if (QFile::exists(absPath)) {
191         path = absPath;
192         return true;
193     }
194 
195     return false;
196 }
197 
parseIncludes(Tokenizer & tokenizer,QList<QString> includedUrls)198 void HRSchemaSerializer::parseIncludes(Tokenizer &tokenizer, QList<QString> includedUrls) {
199     tokenizer.assertToken(Constants::INCLUDE);
200     QString path = tokenizer.take();
201     QString actorId;
202     bool includeAs = false;
203     QString tok = tokenizer.look();
204     if (Constants::INCLUDE_AS == tok) {
205         tokenizer.assertToken(Constants::INCLUDE_AS);
206         includeAs = true;
207         actorId = tokenizer.take();
208     }
209 
210     if (!getAbsoluteIncludePath(path)) {
211         throw ReadFailed(tr("The included file '%1' doesn't exists").arg(path));
212     }
213 
214     // read the file content
215     QString ext = GUrl(path).lastFileSuffix();
216     QString rawData;
217     {
218         QFile file(path);
219         if (!file.open(QIODevice::ReadOnly)) {
220             throw ReadFailed(tr("Can't open '%1'").arg(path));
221         }
222         QTextStream in(&file);
223         in.setCodec("UTF-8");
224         rawData = in.readAll();
225         rawData = rawData.trimmed();
226     }
227 
228     IncludeElementType includeType;
229     ActorPrototype *proto = nullptr;
230     ExternalProcessConfig *cfg = nullptr;
231     Schema *schema = nullptr;
232     QString error;
233 
234     // construct the needed proto from the file content
235     if (isHeaderLine(rawData)) {
236         if ("etc" == ext) {
237             includeType = EXTERNAL_TOOL;
238             cfg = string2Actor(rawData);
239             if (nullptr == cfg) {
240                 throw ReadFailed(tr("File '%1' contains mistakes").arg(path));
241             }
242             if (includeAs) {
243                 cfg->id = actorId;
244             } else {
245                 actorId = cfg->id;
246             }
247             cfg->filePath = path;
248             proto = IncludedProtoFactory::getExternalToolProto(cfg);
249         } else {
250             includeType = SCHEMA;
251             if (includedUrls.contains(path)) {
252                 throw ReadFailed(tr("There is recursive including of the file: '%1'").arg(path));
253             }
254             QList<QString> newUrlList = includedUrls;
255             newUrlList.append(path);
256 
257             schema = new Schema();
258             QMap<ActorId, ActorId> procMap;
259             error = string2Schema(rawData, schema, nullptr, &procMap, newUrlList);
260             if (nullptr != schema && error.isEmpty()) {
261                 if (includeAs) {
262                     schema->setTypeName(actorId);
263                 } else {
264                     actorId = schema->getTypeName();
265                 }
266                 proto = IncludedProtoFactory::getSchemaActorProto(schema, actorId, path);
267             }
268         }
269     } else if (rawData.startsWith(Constants::OLD_XML_HEADER)) {
270         includeType = SCRIPT;
271         proto = ScriptWorkerSerializer::string2actor(rawData, actorId, error, path);
272         if (!includeAs && nullptr != proto) {
273             actorId = proto->getDisplayName();
274         }
275     } else {
276         throw ReadFailed(tr("Unknown file format: '%1'").arg(path));
277     }
278     if (nullptr == proto || !error.isEmpty()) {
279         throw ReadFailed(QString("Included element \"%1\" contains a error: %2").arg(path).arg(error));
280     }
281 
282     // register the new proto
283     if (IncludedProtoFactory::isRegistered(actorId)) {
284         bool isEqualProtos = IncludedProtoFactory::isRegisteredTheSameProto(actorId, proto);
285         if (!isEqualProtos) {
286             throw ReadFailed(QString("Another worker with ID '%1' is already registered: %1").arg(actorId));
287         }
288     } else {
289         WorkflowEnv::getProtoRegistry()->registerProto(BaseActorCategories::CATEGORY_INCLUDES(), proto);
290         if (EXTERNAL_TOOL == includeType) {
291             IncludedProtoFactory::registerExternalToolWorker(cfg);
292         } else if (SCRIPT == includeType) {
293             IncludedProtoFactory::registerScriptWorker(actorId);
294         } else if (SCHEMA == includeType) {
295             WorkflowEnv::getSchemaActorsRegistry()->registerSchema(actorId, schema);
296         }
297     }
298 }
299 
parseBodyHeader(Tokenizer & tokenizer,Metadata * meta,bool needName)300 void HRSchemaSerializer::parseBodyHeader(Tokenizer &tokenizer, Metadata *meta, bool needName) {
301     QString bodyStart = tokenizer.take();
302     if (bodyStart != Constants::BODY_START) {
303         throw ReadFailed(tr("Bad header: expected '%1', got '%2'").arg(Constants::BODY_START).arg(bodyStart));
304     }
305 
306     if (tokenizer.look() == Constants::BLOCK_START) {
307         if (needName) {
308             coreLog.details(tr("Workflow name not specified"));
309         }
310     } else {
311         setIfNotNull<QString>(tokenizer.take(), meta == nullptr ? nullptr : &meta->name);
312     }
313 }
314 
deprecatedUrlAttribute(Actor * proc,const QString & urls)315 void HRSchemaSerializer::deprecatedUrlAttribute(Actor *proc, const QString &urls) {
316     QStringList urlList = urls.split(Constants::SEMICOLON);
317     Dataset dSet;
318     foreach (const QString &url, urlList) {
319         dSet.addUrl(new FileUrlContainer(url));
320     }
321     Attribute *a = proc->getParameter(BaseAttributes::URL_IN_ATTRIBUTE().getId());
322     if (nullptr != a) {
323         QList<Dataset> sets;
324         sets << dSet;
325         a->setAttributeValue(qVariantFromValue<QList<Dataset>>(sets));
326     }
327 }
328 
parseUrlAttribute(const QString attrId,QList<StrStrPair> & blockPairs)329 QList<Dataset> HRSchemaSerializer::parseUrlAttribute(const QString attrId, QList<StrStrPair> &blockPairs) {
330     QList<Dataset> sets;
331     QStringList setBlocks;
332     foreach (const StrStrPair &pair, blockPairs) {
333         if (attrId == pair.first) {
334             setBlocks << pair.second;
335             blockPairs.removeOne(pair);
336         }
337     }
338     for (const QString &block : qAsConst(setBlocks)) {
339         Tokenizer tokenizer;
340         tokenizer.tokenize(block);
341 
342         QString name;
343         QList<URLContainer *> urls;
344         try {
345             while (tokenizer.notEmpty()) {
346                 QString tok = tokenizer.take();
347                 if (Constants::DATASET_NAME == tok) {
348                     tokenizer.assertToken(Constants::EQUALS_SIGN);
349                     name = tokenizer.take();
350                 } else if (Constants::FILE_URL == tok) {
351                     tokenizer.assertToken(Constants::EQUALS_SIGN);
352                     urls << new FileUrlContainer(tokenizer.take());
353                 } else if (Constants::DIRECTORY_URL == tok) {
354                     urls << parseDirectoryUrl(tokenizer);
355                 } else if (Constants::DB_SELECT == tok) {
356                     urls << parseDbSelectUrl(tokenizer);
357                 }
358             }
359 
360             if (name.isEmpty()) {
361                 throw ReadFailed(tr("Url definition does not contain dataset name"));
362             }
363         } catch (const ReadFailed &ex) {
364             for (URLContainer *url : qAsConst(urls)) {
365                 delete url;
366             }
367             throw ReadFailed(ex.what);
368         }
369         Dataset dSet(name);
370         for (URLContainer *url : qAsConst(urls)) {
371             dSet.addUrl(url);
372         }
373         sets << dSet;
374     }
375 
376     return sets;
377 }
378 
createDbObjectUrl(const QString & dbUrl,const qint64 objId,const QString & dataType,const QString & objCachedName)379 URLContainer *HRSchemaSerializer::createDbObjectUrl(const QString &dbUrl, const qint64 objId, const QString &dataType, const QString &objCachedName) {
380     if (-1 == objId) {
381         throw ReadFailed(tr("Database select definition: '%1' expected but not found").arg(Constants::DB_OBJECT_ID));
382     } else if (dataType.isEmpty()) {
383         throw ReadFailed(tr("Database select definition: '%1' expected but not found").arg(Constants::DB_OBJECT_TYPE));
384     } else if (objCachedName.isEmpty()) {
385         throw ReadFailed(tr("Database select definition: '%1' expected but not found").arg(Constants::DB_OBJ_CACHED_NAME));
386     } else {
387         const QString objUrl = SharedDbUrlUtils::createDbObjectUrl(dbUrl, objId, dataType, objCachedName);
388         if (objUrl.isEmpty()) {
389             throw ReadFailed(tr("Database select definition: invalid DB object URL"));
390         }
391         return new DbObjUrlContainer(objUrl);
392     }
393 }
394 
createDbFolderUrl(const QString & dbUrl,const QString & dataType,const QString & path,bool recursive)395 DbFolderUrlContainer *HRSchemaSerializer::createDbFolderUrl(const QString &dbUrl, const QString &dataType, const QString &path, bool recursive) {
396     if (dataType.isEmpty()) {
397         throw ReadFailed(tr("Database select definition: '%1' expected but not found").arg(Constants::DB_OBJECT_TYPE));
398     } else if (path.isEmpty()) {
399         throw ReadFailed(tr("Database select definition: '%1' expected but not found").arg(Constants::PATH));
400     }
401 
402     const QString folderUrl = SharedDbUrlUtils::createDbFolderUrl(dbUrl, path, BaseTypes::toDataType(dataType));
403     if (folderUrl.isEmpty()) {
404         throw ReadFailed(tr("Database select definition: invalid DB folder URL"));
405     }
406     return new DbFolderUrlContainer(folderUrl, QString(), QString(), recursive);
407 }
408 
parseDbSelectUrl(Tokenizer & tokenizer)409 URLContainer *HRSchemaSerializer::parseDbSelectUrl(Tokenizer &tokenizer) {
410     const QString sign = tokenizer.take();
411     if (Constants::BLOCK_START == sign) {
412         ParsedPairs pairs(tokenizer);
413         tokenizer.assertToken(Constants::BLOCK_END);
414         const QString dbUrl = pairs.equalPairs.value(Constants::DB_URL, "");
415 
416         const QString dataType = pairs.equalPairs.value(Constants::DB_OBJECT_TYPE, "");
417         const QString objIdStr = pairs.equalPairs.value(Constants::DB_OBJECT_ID, "");
418 
419         DataTypeValueFactory *numberValueFactory = WorkflowEnv::getDataTypeValueFactoryRegistry()->getById(BaseTypes::NUM_TYPE()->getId());
420         qint64 objId = -1;
421         bool ok = false;
422         const QVariant objIdVar = numberValueFactory->getValueFromString(objIdStr, &ok);
423         if (ok) {
424             objId = objIdVar.value<qint64>();
425         }
426 
427         const QString objCachedName = pairs.equalPairs.value(Constants::DB_OBJ_CACHED_NAME, "");
428 
429         const QString path = pairs.equalPairs.value(Constants::PATH, "");
430         const QString objNameFilter = pairs.equalPairs.value(Constants::DB_OBJ_NAME_FILTER, "");
431         const QString accFilter = pairs.equalPairs.value(Constants::DB_SEQ_ACC_FILTER, "");
432         const QString recursiveStr = pairs.equalPairs.value(Constants::RECURSIVE, "");
433 
434         DataTypeValueFactory *boolValueFactory = WorkflowEnv::getDataTypeValueFactoryRegistry()->getById(BaseTypes::BOOL_TYPE()->getId());
435         bool recursive = false;
436         ok = false;
437         const QVariant recursiveVar = boolValueFactory->getValueFromString(recursiveStr, &ok);
438         if (ok) {
439             recursive = recursiveVar.toBool();
440         }
441 
442         if (dbUrl.isEmpty()) {
443             throw ReadFailed(tr("Database select definition: '%1' expected but not found").arg(Constants::DB_URL));
444         } else if (objId != -1 && !path.isEmpty()) {
445             throw ReadFailed(tr("Database select definition: expected either object or folder definition but both found"));
446         } else if (objId != -1) {
447             return createDbObjectUrl(dbUrl, objId, dataType, objCachedName);
448         } else if (!path.isEmpty()) {
449             DbFolderUrlContainer *result = createDbFolderUrl(dbUrl, dataType, path, recursive);
450             if (!AppContext::isGUIMode()) {
451                 result->setObjNameFilter(objNameFilter);
452                 result->setSequenceAccFilter(accFilter);
453             }
454             return result;
455         } else {
456             throw ReadFailed(tr("Database select definition: '%1' or '%2' expected but neither found").arg(Constants::DB_OBJECT_ID).arg(Constants::PATH));
457         }
458     } else {
459         throw ReadFailed(tr("Database select definition: '%1' is expected, '%2' is found").arg(Constants::BLOCK_START).arg(sign));
460     }
461 }
462 
parseDirectoryUrl(Tokenizer & tokenizer)463 URLContainer *HRSchemaSerializer::parseDirectoryUrl(Tokenizer &tokenizer) {
464     QString sign = tokenizer.take();
465     if (Constants::EQUALS_SIGN == sign) {
466         return new DirUrlContainer(tokenizer.take());
467     } else if (Constants::BLOCK_START == sign) {
468         ParsedPairs pairs(tokenizer);
469         tokenizer.assertToken(Constants::BLOCK_END);
470         QString path = pairs.equalPairs.value(Constants::PATH, "");
471         QString incFilter = pairs.equalPairs.value(Constants::INC_FILTER, "");
472         QString excFilter = pairs.equalPairs.value(Constants::EXC_FILTER, "");
473         QString recursiveStr = pairs.equalPairs.value(Constants::RECURSIVE, "false");
474         DataTypeValueFactory *valueFactory = WorkflowEnv::getDataTypeValueFactoryRegistry()->getById(BaseTypes::BOOL_TYPE()->getId());
475         bool recursive = false;
476         bool ok = false;
477         QVariant v = valueFactory->getValueFromString(recursiveStr, &ok);
478         if (ok) {
479             recursive = v.toBool();
480         }
481 
482         return new DirUrlContainer(path, incFilter, excFilter, recursive);
483     } else {
484         throw ReadFailed(tr("folder url definition: '%1' or '%2' are expected, '%3' is found")
485                              .arg(Constants::BLOCK_START)
486                              .arg(Constants::EQUALS_SIGN)
487                              .arg(sign));
488     }
489 }
490 
parseElementsDefinition(Tokenizer & tokenizer,const QString & actorName,QMap<QString,Actor * > & actorMap,QMap<ActorId,ActorId> * idMap)491 Actor *HRSchemaSerializer::parseElementsDefinition(Tokenizer &tokenizer, const QString &actorName, QMap<QString, Actor *> &actorMap, QMap<ActorId, ActorId> *idMap) {
492     if (actorName.contains(QRegExp("\\s"))) {
493         throw ReadFailed(tr("Element name cannot contain whitespaces: '%1'").arg(actorName));
494     }
495     if (actorName.contains(Constants::DOT)) {
496         throw ReadFailed(tr("Element name cannot contain dots: '%1'").arg(actorName));
497     }
498     if (actorMap.contains(actorName)) {
499         throw ReadFailed(tr("Element '%1' already defined").arg(actorName));
500     }
501 
502     ParsedPairs pairs(tokenizer);
503     QString procType = pairs.equalPairs.take(Constants::TYPE_ATTR);
504     if (procType.isEmpty()) {
505         throw ReadFailed(tr("Type attribute not set for %1 element").arg(actorName));
506     }
507     ActorPrototype *proto = WorkflowEnv::getProtoRegistry()->getProto(SchemaSerializer::getElemType(procType));
508 
509     QString procScriptText = pairs.blockPairs.take(Constants::SCRIPT_ATTR);
510     Actor *proc = nullptr;
511     proc = deprecatedActorsReplacer(actorName, procType, pairs);  // AttributeScript always empty for replaced actors
512     if (proto == nullptr && proc == nullptr) {
513         throw ReadFailed(tr("Unknown type of %1 element: %2").arg(actorName).arg(procType));
514     }
515     if (proc == nullptr) {
516         proc = proto->createInstance(actorName, procScriptText.isEmpty() ? nullptr : new AttributeScript(procScriptText));
517     }
518     actorMap[actorName] = proc;
519 
520     QString procName = pairs.equalPairs.take(Constants::NAME_ATTR);
521     if (procName.isEmpty()) {
522         throw ReadFailed(tr("Name attribute not set for %1 element").arg(actorName));
523     }
524     proc->setLabel(procName);
525 
526     ActorId oldId = str2aid(pairs.equalPairs.take(Constants::ELEM_ID_ATTR));
527     if (idMap != nullptr && !oldId.isEmpty()) {
528         idMap->insert(oldId, proc->getId());
529     }
530 
531     OldUWL::parseOldAttributes(proc, pairs);
532 
533     foreach (const QString &key, pairs.blockPairs.uniqueKeys()) {
534         Attribute *a = proc->getParameter(key);
535         if (nullptr == a) {
536             continue;
537         }
538         if (GROUPER_SLOT_GROUP == a->getGroup()) {
539             parseGrouperOutSlots(proc, pairs.blockPairs.values(key), key);
540         } else if (MARKER_GROUP == a->getGroup()) {
541             parseMarkers(proc, pairs.blockPairs.values(key), key);
542         } else if (a->getAttributeType()->getId() == BaseTypes::URL_DATASETS_TYPE()->getId()) {
543             QList<Dataset> sets = parseUrlAttribute(a->getId(), pairs.blockPairsList);
544             a->setAttributeValue(qVariantFromValue<QList<Dataset>>(sets));
545         } else {
546             proc->getParameter(key)->getAttributeScript().setScriptText(pairs.blockPairs.value(key));
547         }
548     }
549 
550     bool workflowContainsInvalidFormatIds = false;
551     foreach (const QString &key, pairs.equalPairs.keys()) {
552         Attribute *attr = proc->getParameter(key);
553         QString value = pairs.equalPairs.value(key);
554 
555         if (key == BaseAttributes::DOCUMENT_FORMAT_ATTRIBUTE().getId()) {
556             if (BaseDocumentFormats::isInvalidId(value)) {
557                 workflowContainsInvalidFormatIds = true;
558                 value = BaseDocumentFormats::toValidId(value);
559             }
560         }
561 
562         if (nullptr != attr) {
563             attr->setAttributeValue(getAttrValue(proc, key, value));
564         } else {
565             coreLog.details(tr("Unexpected actor attribute: %1").arg(key));
566         }
567     }
568     if (workflowContainsInvalidFormatIds) {
569         GCOUNTER(cvar, "Invalid format IDs: an element was saved with 1.26.0");
570     }
571 
572     foreach (const QString &valDef, pairs.blockPairs.values(Constants::VALIDATOR)) {
573         U2OpStatus2Log os;
574         ValidatorDesc desc = parseValidator(valDef, os);
575         if (!os.hasError()) {
576             proc->addCustomValidator(desc);
577         }
578     }
579     proc->updateItemsAvailability();
580 
581     return proc;
582 }
583 
parseValidator(const QString & desc,U2OpStatus & os)584 ValidatorDesc HRSchemaSerializer::parseValidator(const QString &desc, U2OpStatus &os) {
585     ValidatorDesc result;
586     ParsedPairs pairs(desc, 0);
587     if (!pairs.equalPairs.contains(Constants::V_TYPE)) {
588         os.setError(tr("No validator type"));
589         return result;
590     }
591     result.type = pairs.equalPairs.take(Constants::V_TYPE);
592     int blocks = 0;
593     if (Constants::V_SCRIPT == result.type) {
594         blocks = 1;
595         if (!pairs.blockPairs.contains(Constants::V_SCRIPT)) {
596             os.setError(tr("Script validator has not a script"));
597             return result;
598         }
599     }
600 
601     if (blocks > pairs.blockPairs.size()) {
602         os.setError(tr("Too many blocks in validator definition"));
603         return result;
604     }
605 
606     result.options.unite(pairs.equalPairs);
607     result.options.unite(pairs.blockPairs);
608     return result;
609 }
610 
parseAction(Tokenizer & tokenizer)611 GrouperSlotAction HRSchemaSerializer::parseAction(Tokenizer &tokenizer) {
612     ParsedPairs pairs(tokenizer);
613     tokenizer.assertToken(Constants::BLOCK_END);
614 
615     QString type = pairs.equalPairs.value(Constants::TYPE_ATTR, "");
616     if (type.isEmpty()) {
617         throw ReadFailed(tr("Grouper out slot action: empty type"));
618     } else if (!ActionTypes::isValidType(type)) {
619         throw ReadFailed(tr("Grouper out slot action: invalid type: %1").arg(type));
620     }
621     pairs.equalPairs.take(Constants::TYPE_ATTR);
622 
623     GrouperSlotAction result(type);
624 
625     foreach (const QString &paramId, pairs.equalPairs.keys()) {
626         QString param = pairs.equalPairs.take(paramId);
627         if (!ActionParameters::isValidParameter(type, paramId)) {
628             throw ReadFailed(tr("Grouper out slot action: invalid parameter: %1").arg(paramId));
629         }
630 
631         ActionParameters::ParameterType pType = ActionParameters::getType(paramId);
632         QVariant var;
633         bool ok = false;
634         bool b = false;
635         int num = 0;
636         switch (pType) {
637             case ActionParameters::INTEGER:
638                 num = param.toInt(&ok);
639                 if (!ok) {
640                     throw ReadFailed(tr("Grouper out slot action: bad int '%1' at parameter %2")
641                                          .arg(param)
642                                          .arg(paramId));
643                 }
644                 var = num;
645                 break;
646             case ActionParameters::BOOLEAN:
647                 if ("true" == param) {
648                     b = true;
649                 } else if ("false" == param) {
650                     b = false;
651                 } else {
652                     throw ReadFailed(tr("Grouper out slot action: bad bool '%1' at parameter %2")
653                                          .arg(param)
654                                          .arg(paramId));
655                 }
656                 var = b;
657                 break;
658             case ActionParameters::STRING:
659                 var = param;
660                 break;
661         }
662 
663         result.setParameterValue(paramId, var);
664     }
665 
666     return result;
667 }
668 
parseGrouperOutSlots(Actor * proc,const QStringList & outSlotDefs,const QString & attrId)669 void HRSchemaSerializer::parseGrouperOutSlots(Actor *proc, const QStringList &outSlotDefs, const QString &attrId) {
670     GrouperOutSlotAttribute *attr = dynamic_cast<GrouperOutSlotAttribute *>(proc->getParameter(attrId));
671     Tokenizer tokenizer;
672 
673     QStringList names;
674 
675     foreach (const QString &slotDef, outSlotDefs) {
676         tokenizer.tokenizeSchema(slotDef);
677         QString name;
678         QString inSlot;
679         QScopedPointer<GrouperSlotAction> action(nullptr);
680 
681         while (tokenizer.notEmpty()) {
682             QString tok = tokenizer.take();
683             QString next = tokenizer.take();
684             if (Constants::EQUALS_SIGN == next) {
685                 if (Constants::NAME_ATTR == tok) {
686                     name = tokenizer.take();
687                     if (names.contains(name)) {
688                         throw ReadFailed(tr("Grouper out slot: duplicated slot name: %1").arg(name));
689                     }
690                     names << name;
691                 } else if (Constants::IN_SLOT == tok) {
692                     inSlot = tokenizer.take();
693                 } else {
694                     throw ReadFailed(tr("Grouper out slot: unknown attribute: %1").arg(tok));
695                 }
696             } else if (Constants::BLOCK_START == next) {
697                 if (Constants::ACTION != tok) {
698                     throw ReadFailed(tr("Grouper out slot: unknown block definition: '%1'. %2 expected").arg(tok).arg(Constants::ACTION));
699                 }
700                 action.reset(new GrouperSlotAction(parseAction(tokenizer)));
701             } else {
702                 throw ReadFailed(tr("Grouper out slot: unknown token: '%1'. %2 or %3 expected").arg(next).arg(Constants::BLOCK_START).arg(Constants::EQUALS_SIGN));
703             }
704         }
705 
706         if (name.isEmpty()) {
707             throw ReadFailed(tr("Grouper out slot: empty slot name"));
708         }
709         if (inSlot.isEmpty()) {
710             throw ReadFailed(tr("Grouper out slot: empty in-slot"));
711         }
712 
713         GrouperOutSlot slot(name, inSlot);
714         if (!action.isNull()) {
715             slot.setAction(*action.data());
716         }
717         attr->addOutSlot(slot);
718 
719         Port *outPort = proc->getOutputPorts().at(0);
720         assert(outPort->getOutputType()->isMap());
721         QMap<Descriptor, DataTypePtr> outTypeMap = outPort->getOutputType()->getDatatypesMap();
722         Descriptor newTmpSlot = Descriptor(name, name, name);
723         outTypeMap[newTmpSlot] = ActionTypes::getDataTypeByAction(!action.isNull() ? action->getType() : "");
724         DataTypePtr newType(new MapDataType(dynamic_cast<Descriptor &>(*(outPort->getType())), outTypeMap));
725         outPort->setNewType(newType);
726     }
727 }
728 
finalizeGrouperSlots(const QMap<QString,Actor * > & actorMap)729 void HRSchemaSerializer::finalizeGrouperSlots(const QMap<QString, Actor *> &actorMap) {
730     foreach (Actor *p, actorMap.values()) {
731         if (p->getId() != "grouper") {  // TODO: fix it
732             continue;
733         }
734 
735         // check incoming slots
736     }
737 }
738 
parseAt(const QString & dottedStr,int ind)739 QString HRSchemaSerializer::parseAt(const QString &dottedStr, int ind) {
740     QStringList list = dottedStr.split(Constants::DOT);
741     return list.size() > ind ? list.at(ind) : "";
742 }
743 
parseAfter(const QString & dottedStr,int ind)744 QString HRSchemaSerializer::parseAfter(const QString &dottedStr, int ind) {
745     QStringList list = dottedStr.split(Constants::DOT);
746     QString res;
747     for (int i = ind + 1; i < list.size(); ++i) {
748         res += list.at(i) + Constants::DOT;
749     }
750     return res.mid(0, res.size() - Constants::DOT.size());
751 }
752 
parseIteration(Tokenizer & tokenizer,const QMap<QString,Actor * > & actorMap,bool pasteMode)753 QMap<ActorId, QVariantMap> HRSchemaSerializer::parseIteration(Tokenizer &tokenizer,
754                                                               const QMap<QString, Actor *> &actorMap,
755                                                               bool pasteMode) {
756     QPair<QString, QString> idPair = ParsedPairs::parseOneEqual(tokenizer);
757     if (idPair.first != Constants::ITERATION_ID) {
758         throw ReadFailed(tr("%1 definition expected at .iterations block").arg(Constants::ITERATION_ID));
759     }
760 
761     QMap<ActorId, QVariantMap> cfg;
762     bool ok = false;
763     idPair.second.toInt(&ok);
764     if (!ok) {
765         throw ReadFailed(tr("Cannot parse integer from '%1': iteration id").arg(idPair.second));
766     }
767 
768     while (tokenizer.look() != Constants::BLOCK_END) {
769         QString actorName = tokenizer.take();
770         if (!actorMap.contains(actorName)) {
771             if (!pasteMode) {
772                 throw ReadFailed(tr("Element id '%1' undefined in .iteration block").arg(actorName));
773             } else {
774                 tokenizer.assertToken(Constants::BLOCK_START);
775                 ParsedPairs::skipBlock(tokenizer);
776                 continue;
777             }
778         }
779 
780         tokenizer.assertToken(Constants::BLOCK_START);
781         ParsedPairs pairs(tokenizer, true /*bigBlocks*/);
782         tokenizer.assertToken(Constants::BLOCK_END);
783 
784         QString actorId = actorMap[actorName]->getId();
785         foreach (Attribute *attr, actorMap[actorName]->getParameters()) {
786             QString attrId = attr->getId();
787             if (pairs.equalPairs.contains(attrId)) {
788                 cfg[actorId][attrId] =
789                     getAttrValue(actorMap[actorName], attrId, pairs.equalPairs[attrId]);
790             }
791             if (attr->getAttributeType()->getId() != BaseTypes::URL_DATASETS_TYPE()->getId()) {
792                 continue;
793             }
794             QList<Dataset> sets = parseUrlAttribute(attrId, pairs.blockPairsList);
795             if (!sets.isEmpty()) {
796                 cfg[actorId][attrId] = qVariantFromValue(sets);
797             }
798         }
799     }
800     return cfg;
801 }
802 
tryToConnect(Schema * schema,Port * input,Port * output)803 static void tryToConnect(Schema *schema, Port *input, Port *output) {
804     if (!input || !output || !input->canBind(output) || WorkflowUtils::isPathExist(input, output)) {
805         throw ReadFailed(HRSchemaSerializer::tr("Cannot bind %1:%2 to %3:%4").arg(input->owner()->getId()).arg(input->getId()).arg(output->owner()->getId()).arg(output->getId()));
806     }
807     schema->addFlow(new Link(input, output));
808 }
809 
parseActorBindings(Tokenizer & tokenizer,WorkflowSchemaReaderData & data)810 void HRSchemaSerializer::parseActorBindings(Tokenizer &tokenizer, WorkflowSchemaReaderData &data) {
811     const ActorBindingsGraph &graph = data.schema->getActorBindingsGraph();
812 
813     if (!graph.isEmpty()) {
814         throw ReadFailed(HRSchemaSerializer::tr("Links list is not empty. Maybe .meta is defined earlier than actor-bindings"));
815     }
816 
817     while (tokenizer.look() != Constants::BLOCK_END) {
818         QString from = tokenizer.take();
819         QString srcActorName = HRSchemaSerializer::parseAt(from, 0);
820         Actor *srcActor = data.actorMap.value(srcActorName);
821         if (srcActor == nullptr) {
822             throw ReadFailed(HRSchemaSerializer::tr("Undefined element id: '%1'").arg(srcActorName));
823         }
824         QString srcPortId = HRSchemaSerializer::parseAt(from, 1);
825         Port *srcPort = srcActor->getPort(srcPortId);
826         if (srcPort == nullptr) {
827             throw ReadFailed(HRSchemaSerializer::tr("Cannot find '%1' port at '%2'").arg(srcPortId).arg(srcActorName));
828         }
829 
830         tokenizer.assertToken(Constants::DATAFLOW_SIGN);
831         QString to = tokenizer.take();
832         QString dstActorName = HRSchemaSerializer::parseAt(to, 0);
833         Actor *dstActor = data.actorMap.value(dstActorName);
834         if (dstActor == nullptr) {
835             throw ReadFailed(HRSchemaSerializer::tr("Undefined element id: '%1'").arg(dstActorName));
836         }
837         QString dstPortId = HRSchemaSerializer::parseAt(to, 1);
838         Port *dstPort = dstActor->getPort(dstPortId);
839         if (dstPort == nullptr) {
840             throw ReadFailed(HRSchemaSerializer::tr("Cannot find '%1' port at '%2'").arg(dstPortId).arg(dstActorName));
841         }
842 
843         tryToConnect(data.schema, srcPort, dstPort);
844     }
845 
846     data.defineGraph();
847 
848     QString message;
849     if (!graph.validateGraph(message)) {
850         throw ReadFailed(tr("Validating actor bindings graph failed: '%1'").arg(message));
851     }
852 }
853 
parseAndCheckParameterAlias(const QString & paramString,const QMap<QString,Actor * > & actorMap,QString & actorName,QString & paramId)854 void parseAndCheckParameterAlias(const QString &paramString, const QMap<QString, Actor *> &actorMap, QString &actorName, QString &paramId) {
855     actorName = HRSchemaSerializer::parseAt(paramString, 0);
856     Actor *actor = actorMap.value(actorName);
857     if (actor == nullptr) {
858         throw ReadFailed(
859             HRSchemaSerializer::tr("%1 element is undefined: at \"%2\" in aliases block").arg(actorName).arg(paramString));
860     }
861 
862     paramId = HRSchemaSerializer::parseAt(paramString, 1);
863     Attribute *attr = actor->getParameter(paramId);
864     if (nullptr == attr) {
865         throw ReadFailed(
866             HRSchemaSerializer::tr("%1 parameter is undefined: at \"%2\" in aliases block").arg(paramId).arg(paramString));
867     }
868 }
869 
parseParameterAliases(Tokenizer & tokenizer,const QMap<QString,Actor * > & actorMap)870 void HRSchemaSerializer::parseParameterAliases(Tokenizer &tokenizer, const QMap<QString, Actor *> &actorMap) {
871     QList<QString> newParamNames;  // keeps all unique parameters aliases
872     QList<QString> paramStrings;  // keeps all unique aliased parameters
873 
874     while (tokenizer.look() != Constants::BLOCK_END) {
875         QString paramString = tokenizer.take();
876         tokenizer.assertToken(Constants::BLOCK_START);
877         if (paramStrings.contains(paramString)) {
878             throw ReadFailed(tr("Duplicate parameter alias \"%1\"").arg(paramString));
879         }
880         paramStrings.append(paramString);
881 
882         QString actorName;
883         QString paramId;
884         parseAndCheckParameterAlias(paramString, actorMap, actorName, paramId);
885 
886         ParsedPairs pairs(tokenizer);
887         if (!pairs.blockPairs.isEmpty()) {
888             throw ReadFailed(tr("Empty parameter alias block: \"%1\"").arg(paramString));
889         }
890 
891         QString alias = pairs.equalPairs.take(Constants::ALIAS);
892         if (alias.isEmpty()) {
893             alias = paramString;
894             alias.replace(Constants::DOT, "_at_");
895         }
896         if (newParamNames.contains(alias)) {
897             throw ReadFailed(tr("Duplicate parameter alias name \"%1\" at \"%2\"").arg(alias).arg(paramString));
898         }
899         newParamNames.append(alias);
900 
901         QString descr = pairs.equalPairs.take(Constants::DESCRIPTION);
902 
903         Actor *actor = actorMap[actorName];
904         actor->getParamAliases()[paramId] = alias;
905         actor->getAliasHelp()[alias] = descr;
906 
907         tokenizer.assertToken(Constants::BLOCK_END);
908     }
909 }
910 
911 // -------------- backward compatibility --------------
parseOldAliases(Tokenizer & tokenizer,const QMap<QString,Actor * > & actorMap)912 void HRSchemaSerializer::parseOldAliases(Tokenizer &tokenizer, const QMap<QString, Actor *> &actorMap) {
913     ParsedPairs pairs(tokenizer);
914     if (!pairs.blockPairs.isEmpty()) {
915         throw ReadFailed(tr("No other blocks allowed in alias block"));
916     }
917     foreach (const QString &key, pairs.equalPairs.keys()) {
918         QString actorName = parseAt(key, 0);
919         Actor *actor = actorMap.value(actorName);
920         if (actor == nullptr) {
921             throw ReadFailed(tr("%1 element undefined in aliases block").arg(actorName));
922         }
923         QString attributeId = parseAt(key, 1);
924         if (!actor->hasParameter(attributeId)) {
925             throw ReadFailed(tr("%1 has no parameter %2: in aliases block").arg(actorName).arg(attributeId));
926         }
927         actor->getParamAliases()[attributeId] = pairs.equalPairs.value(key);
928     }
929 }
930 
parseAliasesHelp(Tokenizer & tokenizer,const QList<Actor * > & procs)931 void HRSchemaSerializer::parseAliasesHelp(Tokenizer &tokenizer, const QList<Actor *> &procs) {
932     ParsedPairs pairs(tokenizer);
933     if (!pairs.blockPairs.isEmpty()) {
934         throw ReadFailed(tr("No other blocks allowed in help block"));
935     }
936 
937     foreach (const QString &key, pairs.equalPairs.keys()) {
938         QString paramName;
939         Actor *actor = WorkflowUtils::findActorByParamAlias(procs, key, paramName, false);
940         if (actor == nullptr) {
941             throw ReadFailed(tr("Undefined parameter alias used in help block: '%1'").arg(key));
942         }
943         QString help = pairs.equalPairs.value(key);
944         assert(!help.isEmpty());
945         actor->getAliasHelp()[key] = help;
946     }
947 }
948 // ----------------------------------------------------
949 
parseAndCheckPortAlias(const QString & portString,const QMap<QString,Actor * > & actorMap,QString & actorName,QString & portId)950 void parseAndCheckPortAlias(const QString &portString, const QMap<QString, Actor *> &actorMap, QString &actorName, QString &portId) {
951     actorName = HRSchemaSerializer::parseAt(portString, 0);
952     Actor *actor = actorMap.value(actorName);
953     if (actor == nullptr) {
954         throw ReadFailed(
955             HRSchemaSerializer::tr("%1 element is undefined: at \"%2\" in aliases block").arg(actorName).arg(portString));
956     }
957 
958     portId = HRSchemaSerializer::parseAt(portString, 1);
959     Port *port = actor->getPort(portId);
960     if (nullptr == port) {
961         throw ReadFailed(
962             HRSchemaSerializer::tr("%1 port is undefined: at \"%2\" in aliases block").arg(portId).arg(portString));
963     }
964 }
965 
parseSlotAlias(const QString & slotString,const QMap<QString,Actor * > & actorMap,QString & actorName,QString & portId,QString & slotId)966 void parseSlotAlias(const QString &slotString, const QMap<QString, Actor *> &actorMap, QString &actorName, QString &portId, QString &slotId) {
967     parseAndCheckPortAlias(slotString, actorMap, actorName, portId);
968 
969     slotId = HRSchemaSerializer::parseAt(slotString, 2);
970     DataTypePtr dt = actorMap.value(actorName)->getPort(portId)->Port::getType();
971     QList<Descriptor> descs = dt->getAllDescriptors();
972     if (!descs.contains(slotId)) {
973         throw ReadFailed(
974             HRSchemaSerializer::tr("%1 slot is undefined: at \"%2\" in aliases block'").arg(slotId).arg(slotString));
975     }
976 }
977 
parsePortAliases(Tokenizer & tokenizer,const QMap<QString,Actor * > & actorMap,QList<PortAlias> & portAliases)978 void HRSchemaSerializer::parsePortAliases(Tokenizer &tokenizer, const QMap<QString, Actor *> &actorMap, QList<PortAlias> &portAliases) {
979     QList<QString> newPortNames;  // keeps all unique ports aliases
980     QList<QString> portStrings;  // keeps all unique aliased ports
981 
982     while (tokenizer.look() != Constants::BLOCK_END) {
983         QString portString = tokenizer.take();
984         if (portStrings.contains(portString)) {
985             throw ReadFailed(tr("Duplicate port alias \"%1\"").arg(portString));
986         }
987         portStrings.append(portString);
988 
989         QString sourceActorName;
990         QString sourcePortId;
991         parseAndCheckPortAlias(portString, actorMap, sourceActorName, sourcePortId);
992         tokenizer.assertToken(Constants::BLOCK_START);
993 
994         ParsedPairs pairs(tokenizer);
995         if (!pairs.blockPairs.isEmpty()) {
996             throw ReadFailed(tr("Empty port aliases are not allowed: %1").arg(portString));
997         }
998 
999         QString alias = pairs.equalPairs.take(Constants::ALIAS);
1000         if (alias.isEmpty()) {
1001             alias = portString;
1002             alias.replace(Constants::DOT, "_at_");
1003         }
1004         if (newPortNames.contains(alias)) {
1005             throw ReadFailed(tr("Duplicate port alias name \"%1\" at \"%2\"").arg(alias).arg(portString));
1006         }
1007         newPortNames.append(alias);
1008 
1009         QString descr = pairs.equalPairs.take(Constants::DESCRIPTION);
1010         if (descr.isEmpty()) {
1011             descr = alias;
1012         }
1013 
1014         PortAlias newPortAlias(actorMap[sourceActorName]->getPort(sourcePortId), alias, descr);
1015 
1016         foreach (const QString &slotString, pairs.equalPairs.keys()) {
1017             QString actorName;
1018             QString portId;
1019             QString slotId;
1020             parseSlotAlias(slotString, actorMap, actorName, portId, slotId);
1021             Port *port = actorMap[actorName]->getPort(portId);
1022 
1023             QString newSlotId = pairs.equalPairs.value(slotString);
1024             if (!newPortAlias.addSlot(port, slotId, newSlotId)) {
1025                 throw ReadFailed(tr("Duplicate slot alias \"%1\" at port alias\"%2\"").arg(slotString).arg(portString));
1026             }
1027         }
1028         portAliases.append(newPortAlias);
1029         tokenizer.assertToken(Constants::BLOCK_END);
1030     }
1031 }
1032 
parseDataflow(Tokenizer & tokenizer,const QString & srcTok,const QMap<QString,Actor * > & actorMap)1033 QPair<Port *, Port *> HRSchemaSerializer::parseDataflow(Tokenizer &tokenizer, const QString &srcTok, const QMap<QString, Actor *> &actorMap) {
1034     QString srcActorName = parseAt(srcTok, 0);
1035     QString srcSlotId = parseAfter(srcTok, 0);
1036     if (!actorMap.contains(srcActorName)) {
1037         throw ReadFailed(tr("Undefined element id '%1' at '%2'").arg(srcActorName).arg(srcTok));
1038     }
1039     bool slotFound = false;
1040     Port *srcPort = nullptr;
1041     foreach (Port *port, actorMap.value(srcActorName)->getEnabledOutputPorts()) {
1042         DataTypePtr dt = port->Port::getType();
1043         QList<Descriptor> descs = dt->getAllDescriptors();
1044         descs << *dt;
1045         slotFound = slotFound || descs.contains(srcSlotId);
1046         if (slotFound) {
1047             srcPort = port;
1048             break;
1049         }
1050     }
1051     if (!slotFound) {
1052         throw ReadFailed(tr("Undefined slot id '%1' at '%2'").arg(srcSlotId).arg(srcTok));
1053     }
1054 
1055     tokenizer.assertToken(Constants::DATAFLOW_SIGN);  // "->"
1056     QString destTok = tokenizer.take();
1057     QString destActorName = parseAt(destTok, 0);
1058     QString destPortId = parseAt(destTok, 1);
1059     QString destSlotId = parseAfter(destTok, 1);
1060     if (!actorMap.contains(destActorName)) {
1061         throw ReadFailed(tr("Undefined element id '%1' at '%2'").arg(destActorName).arg(destTok));
1062     }
1063 
1064     Port *destPort = actorMap.value(destActorName)->getPort(destPortId);
1065     if (destPort == nullptr) {
1066         throw ReadFailed(tr("Undefined port id '%1' at '%2'").arg(destPortId).arg(destTok));
1067     }
1068     if (!destPort->isInput()) {
1069         throw ReadFailed(tr("Destination port should be input: %1").arg(destPortId));
1070     }
1071 
1072     DataTypePtr dt = destPort->Port::getType();
1073     QList<Descriptor> descs = dt->getAllDescriptors();
1074     descs << *dt;
1075     if (!descs.contains(destSlotId)) {
1076         throw ReadFailed(tr("Undefined slot id '%1' at '%2'").arg(destSlotId).arg(destTok));
1077     }
1078 
1079     IntegralBusPort *bus = qobject_cast<IntegralBusPort *>(destPort);
1080     IntegralBusSlot slot(srcSlotId, "", actorMap.value(srcActorName)->getId());
1081     bus->setBusMapValue(destSlotId, slot.toString());
1082 
1083     QString token = tokenizer.look();
1084     if (Constants::BLOCK_START == token) {
1085         tokenizer.assertToken(Constants::BLOCK_START);
1086         ParsedPairs pairs(tokenizer);
1087         tokenizer.assertToken(Constants::BLOCK_END);
1088 
1089         if (pairs.equalPairs.contains(Constants::PATH_THROUGH)) {
1090             QStringList path;
1091             QString value = pairs.equalPairs.take(Constants::PATH_THROUGH);
1092             foreach (QString p, value.split(",")) {
1093                 p = p.trimmed();
1094                 if (!actorMap.contains(p)) {
1095                     throw ReadFailed(tr("Undefined actor id '%1' at '%2'").arg(p).arg(value));
1096                 }
1097                 path.append(p);
1098             }
1099 
1100             bus->addPathBySlotsPair(destSlotId, slot.toString(), path);
1101         }
1102     }
1103     return QPair<Port *, Port *>(srcPort, destPort);
1104 }
1105 
parseMeta(WorkflowSchemaReaderData & data)1106 static void parseMeta(WorkflowSchemaReaderData &data) {
1107     QString tok = data.tokenizer.look();
1108     if (Constants::BLOCK_START != tok) {
1109         data.schema->setTypeName(tok);
1110         data.tokenizer.take();
1111     }
1112 
1113     data.tokenizer.assertToken(Constants::BLOCK_START);
1114     while (data.tokenizer.look() != Constants::BLOCK_END) {
1115         tok = data.tokenizer.take();
1116         if (Constants::PARAM_ALIASES_START == tok) {
1117             data.tokenizer.assertToken(Constants::BLOCK_START);
1118             HRSchemaSerializer::parseParameterAliases(data.tokenizer, data.actorMap);
1119             data.tokenizer.assertToken(Constants::BLOCK_END);
1120         } else if (Constants::PORT_ALIASES_START == tok) {
1121             data.tokenizer.assertToken(Constants::BLOCK_START);
1122             HRSchemaSerializer::parsePortAliases(data.tokenizer, data.actorMap, data.portAliases);
1123             data.tokenizer.assertToken(Constants::BLOCK_END);
1124         } else if (Constants::VISUAL_START == tok) {
1125             data.tokenizer.assertToken(Constants::BLOCK_START);
1126             if (nullptr == data.meta) {
1127                 ParsedPairs::skipBlock(data.tokenizer);
1128             } else {
1129                 HRVisualParser vp(data);
1130                 U2OpStatus2Log os;
1131                 vp.parse(os);
1132                 if (os.hasError()) {
1133                     data.meta->resetVisual();
1134                 }
1135                 data.tokenizer.assertToken(Constants::BLOCK_END);
1136             }
1137         } else if (Constants::OLD_ALIASES_START == tok) {
1138             data.tokenizer.assertToken(Constants::BLOCK_START);
1139             HRSchemaSerializer::parseOldAliases(data.tokenizer, data.actorMap);
1140             data.tokenizer.assertToken(Constants::BLOCK_END);
1141         } else if (Constants::ALIASES_HELP_START == tok) {
1142             data.tokenizer.assertToken(Constants::BLOCK_START);
1143             HRSchemaSerializer::parseAliasesHelp(data.tokenizer, data.actorMap.values());
1144             data.tokenizer.assertToken(Constants::BLOCK_END);
1145         } else if (HRWizardParser::WIZARD == tok) {
1146             data.tokenizer.assertToken(Constants::BLOCK_START);
1147             HRWizardParser ws(data.tokenizer, data.actorMap);
1148             U2OpStatusImpl os;
1149             Wizard *w = ws.parseWizard(os);
1150             CHECK_OP_EXT(os, throw ReadFailed(os.getError()), );
1151             data.wizards << w;
1152             data.tokenizer.assertToken(Constants::BLOCK_END);
1153         } else if (Constants::ESTIMATIONS == tok) {
1154             data.tokenizer.assertToken(Constants::BLOCK_START);
1155             QString code = data.tokenizer.take();
1156             if (nullptr != data.meta) {
1157                 data.meta->estimationsCode = code;
1158             }
1159             data.tokenizer.assertToken(Constants::BLOCK_END);
1160         } else {
1161             throw ReadFailed(Constants::UNDEFINED_META_BLOCK.arg(tok));
1162         }
1163     }
1164 }
1165 
parseBody(WorkflowSchemaReaderData & data)1166 static void parseBody(WorkflowSchemaReaderData &data) {
1167     Tokenizer &tokenizer = data.tokenizer;
1168     while (tokenizer.notEmpty() && tokenizer.look() != Constants::BLOCK_END) {
1169         QString tok = tokenizer.take();
1170         QString next = tokenizer.look();
1171         if (tok == Constants::META_START) {
1172             parseMeta(data);
1173             tokenizer.assertToken(Constants::BLOCK_END);
1174         } else if (tok == Constants::DOT_ITERATION_START) {
1175             QString itName = tokenizer.look() == Constants::BLOCK_START ? "" : tokenizer.take();
1176             tokenizer.assertToken(Constants::BLOCK_START);
1177             QMap<ActorId, QVariantMap> cfg = HRSchemaSerializer::parseIteration(tokenizer, data.actorMap);
1178             data.schema->applyConfiguration(cfg);
1179             tokenizer.assertToken(Constants::BLOCK_END);
1180         } else if (tok == Constants::ACTOR_BINDINGS) {
1181             tokenizer.assertToken(Constants::BLOCK_START);
1182             HRSchemaSerializer::parseActorBindings(tokenizer, data);
1183             tokenizer.assertToken(Constants::BLOCK_END);
1184         } else if (tok == OldConstants::MARKER_START) {
1185             OldUWL::parseMarkerDefinition(tokenizer, data.actorMap);
1186             tokenizer.assertToken(Constants::BLOCK_END);
1187         } else if (next == Constants::DATAFLOW_SIGN) {
1188             data.dataflowLinks << HRSchemaSerializer::parseDataflow(tokenizer, tok, data.actorMap);
1189         } else if (next == Constants::BLOCK_START) {
1190             tokenizer.take();
1191             Actor *proc = HRSchemaSerializer::parseElementsDefinition(tokenizer, tok, data.actorMap, data.idMap);
1192             data.schema->addProcess(proc);
1193             proc->updateDelegateTags();
1194             tokenizer.assertToken(Constants::BLOCK_END);
1195         } else {
1196             throw ReadFailed(Constants::UNDEFINED_CONSTRUCT.arg(tok).arg(next));
1197         }
1198     }
1199 
1200     foreach (Actor *proc, data.actorMap.values()) {
1201         ActorPrototype *proto = proc->getProto();
1202         if (nullptr != proto->getEditor()) {
1203             ActorConfigurationEditor *actorEd = dynamic_cast<ActorConfigurationEditor *>(proto->getEditor());
1204             if (nullptr != actorEd) {
1205                 ActorConfigurationEditor *editor = dynamic_cast<ActorConfigurationEditor *>(proto->getEditor()->clone());
1206                 editor->setConfiguration(proc);
1207                 proc->setEditor(editor);
1208             }
1209         }
1210     }
1211 }
1212 
setFlows(WorkflowSchemaReaderData & data)1213 static void setFlows(WorkflowSchemaReaderData &data) {
1214     if (data.isGraphDefined()) {
1215         return;
1216     }
1217     if (!data.links.isEmpty()) {
1218         QList<QPair<Port *, Port *>>::iterator i = data.links.begin();
1219         for (; i != data.links.end(); i++) {
1220             tryToConnect(data.schema, i->first, i->second);
1221         }
1222     } else {
1223         FlowGraph graph(data.dataflowLinks);
1224         graph.minimize();
1225         foreach (Port *input, graph.graph.keys()) {
1226             foreach (Port *output, graph.graph.value(input)) {
1227                 tryToConnect(data.schema, input, output);
1228             }
1229         }
1230     }
1231 }
1232 
addEmptyValsToBindings(const QList<Actor * > & procs)1233 void HRSchemaSerializer::addEmptyValsToBindings(const QList<Actor *> &procs) {
1234     foreach (Actor *actor, procs) {
1235         foreach (Port *p, actor->getInputPorts()) {
1236             IntegralBusPort *port = qobject_cast<IntegralBusPort *>(p);
1237             StrStrMap busMap = port->getParameter(IntegralBusPort::BUS_MAP_ATTR_ID)->getAttributeValueWithoutScript<StrStrMap>();
1238             DataTypePtr t = port->Port::getType();
1239             assert(t->isMap());
1240             QMap<Descriptor, DataTypePtr> typeMap = t->getDatatypesMap();
1241             foreach (const Descriptor &d, typeMap.keys()) {
1242                 if (!busMap.contains(d.getId())) {
1243                     port->setBusMapValue(d.getId(), "");
1244                 }
1245             }
1246         }
1247     }
1248 }
1249 
string2Schema(const QString & bytes,Schema * schema,Metadata * meta,QMap<ActorId,ActorId> * idMap,QList<QString> includedUrls)1250 QString HRSchemaSerializer::string2Schema(const QString &bytes, Schema *schema, Metadata *meta, QMap<ActorId, ActorId> *idMap, QList<QString> includedUrls) {
1251     try {
1252         WorkflowSchemaReaderData data(bytes, schema, meta, idMap);
1253         parseHeader(data.tokenizer, data.meta);
1254         data.tokenizer.removeCommentTokens();
1255 
1256         QString tok = data.tokenizer.look();
1257         while (Constants::INCLUDE == tok) {
1258             parseIncludes(data.tokenizer, includedUrls);
1259             tok = data.tokenizer.look();
1260         }
1261         parseBodyHeader(data.tokenizer, data.meta);
1262 
1263         if (schema != nullptr) {
1264             data.tokenizer.assertToken(Constants::BLOCK_START);
1265             parseBody(data);
1266             data.tokenizer.assertToken(Constants::BLOCK_END);
1267             setFlows(data);
1268             addEmptyValsToBindings(data.actorMap.values());
1269             data.schema->setPortAliases(data.portAliases);
1270             data.schema->setWizards(data.wizards);
1271         }
1272     } catch (const ReadFailed &ex) {
1273         return ex.what;
1274     } catch (...) {
1275         return Constants::UNKNOWN_ERROR;
1276     }
1277     postProcessing(schema);
1278     return Constants::NO_ERROR;
1279 }
1280 
postProcessing(Schema * schema)1281 void HRSchemaSerializer::postProcessing(Schema *schema) {
1282     CHECK(schema != nullptr, );
1283 
1284     foreach (Actor *a, schema->getProcesses()) {
1285         CHECK(a != nullptr, );
1286         ActorPrototype *proto = a->getProto();
1287         CHECK(proto != nullptr, );
1288         foreach (Attribute *attr, proto->getAttributes()) {
1289             CHECK(attr != nullptr, );
1290             foreach (PortRelationDescriptor *pd, attr->getPortRelations()) {
1291                 Port *p = a->getPort(pd->getPortId());
1292                 CHECK(p != nullptr, );
1293                 CHECK(a->hasParameter(attr->getId()), );
1294                 QVariant value = a->getParameter(attr->getId())->getAttributePureValue();
1295                 if (!p->getLinks().isEmpty() && !pd->isPortEnabled(value)) {
1296                     a->setParameter(attr->getId(), pd->getValuesWithEnabledPort().first());
1297                 }
1298             }
1299         }
1300     }
1301 }
1302 
parsePorts(Tokenizer & tokenizer,QList<DataConfig> & ports)1303 void HRSchemaSerializer::parsePorts(Tokenizer &tokenizer, QList<DataConfig> &ports) {
1304     while (tokenizer.look() != Constants::BLOCK_END) {
1305         DataConfig cfg;
1306         cfg.attributeId = tokenizer.take();
1307         tokenizer.assertToken(Constants::BLOCK_START);
1308         ParsedPairs pairs(tokenizer);
1309         cfg.attrName = pairs.equalPairs.take(Constants::NAME_ATTR);
1310         cfg.type = pairs.equalPairs.take(Constants::TYPE_PORT);
1311         cfg.format = pairs.equalPairs.take(Constants::FORMAT_PORT);
1312         cfg.description = pairs.equalPairs.take(Constants::DESCRIPTION);
1313         tokenizer.assertToken(Constants::BLOCK_END);
1314 
1315         if (cfg.attrName.isEmpty()) {
1316             cfg.attrName = cfg.attributeId;
1317         }
1318 
1319         ports << cfg;
1320     }
1321 }
1322 
parseAttributes(Tokenizer & tokenizer,QList<AttributeConfig> & attrs)1323 void HRSchemaSerializer::parseAttributes(Tokenizer &tokenizer, QList<AttributeConfig> &attrs) {
1324     while (tokenizer.look() != Constants::BLOCK_END) {
1325         AttributeConfig cfg;
1326         cfg.attributeId = tokenizer.take();
1327         tokenizer.assertToken(Constants::BLOCK_START);
1328         ParsedPairs pairs(tokenizer);
1329         cfg.attrName = pairs.equalPairs.take(Constants::NAME_ATTR);
1330         cfg.type = pairs.equalPairs.take(Constants::TYPE_PORT);
1331         cfg.defaultValue = pairs.equalPairs.take(Constants::DEFAULT_VALUE);
1332         cfg.description = pairs.equalPairs.take(Constants::DESCRIPTION);
1333         if (0 == QString::compare(pairs.equalPairs.take(Constants::ADD_TO_DASHBOARD), Constants::TRUE)) {
1334             cfg.flags |= AttributeConfig::AddToDashboard;
1335         }
1336         if (0 == QString::compare(pairs.equalPairs.take(Constants::OPEN_WITH_UGENE), Constants::TRUE)) {
1337             cfg.flags |= AttributeConfig::OpenWithUgene;
1338         }
1339         tokenizer.assertToken(Constants::BLOCK_END);
1340         if (cfg.attrName.isEmpty()) {
1341             cfg.attrName = cfg.attributeId;
1342         }
1343         cfg.fixTypes();
1344         attrs << cfg;
1345     }
1346 }
1347 
parseActorBody(Tokenizer & tokenizer)1348 ExternalProcessConfig *HRSchemaSerializer::parseActorBody(Tokenizer &tokenizer) {
1349     ExternalProcessConfig *cfg = new ExternalProcessConfig();
1350     cfg->id = tokenizer.take();
1351     while (tokenizer.notEmpty() && tokenizer.look() != Constants::BLOCK_END) {
1352         QString tok = tokenizer.take();
1353         QString next = tokenizer.look();
1354         if (tok == Constants::INPUT_START) {
1355             tokenizer.assertToken(Constants::BLOCK_START);
1356             HRSchemaSerializer::parsePorts(tokenizer, cfg->inputs);
1357             tokenizer.assertToken(Constants::BLOCK_END);
1358         } else if (tok == Constants::OUTPUT_START) {
1359             tokenizer.assertToken(Constants::BLOCK_START);
1360             HRSchemaSerializer::parsePorts(tokenizer, cfg->outputs);
1361             tokenizer.assertToken(Constants::BLOCK_END);
1362         } else if (tok == Constants::ATTRIBUTES_START) {
1363             tokenizer.assertToken(Constants::BLOCK_START);
1364             HRSchemaSerializer::parseAttributes(tokenizer, cfg->attrs);
1365             tokenizer.assertToken(Constants::BLOCK_END);
1366         } else if (tok == Constants::BLOCK_START) {
1367             // tokenizer.take();
1368             /*Actor * proc = HRSchemaSerializer::parseElementsDefinition(tokenizer, tok, data.actorMap, data.idMap);
1369             data.schema->addProcess(proc);
1370             tokenizer.assertToken(HRSchemaSerializer::BLOCK_END);*/
1371         } else if (tok == Constants::NAME_ATTR) {
1372             tokenizer.assertToken(Constants::COLON);
1373             cfg->name = tokenizer.take();
1374         } else if (tok == Constants::USE_INTEGRATED_TOOL) {
1375             tokenizer.assertToken(Constants::COLON);
1376             cfg->useIntegratedTool = (0 != QString::compare(tokenizer.take(), Constants::FALSE, Qt::CaseInsensitive));
1377         } else if (tok == Constants::CUSTOM_TOOL_PATH) {
1378             tokenizer.assertToken(Constants::COLON);
1379             cfg->customToolPath = tokenizer.take();
1380         } else if (tok == Constants::INTEGRATED_TOOL_ID) {
1381             tokenizer.assertToken(Constants::COLON);
1382             cfg->integratedToolId = tokenizer.take();
1383         } else if (tok == Constants::CMDLINE) {
1384             tokenizer.assertToken(Constants::COLON);
1385             cfg->cmdLine = tokenizer.take();
1386         } else if (tok == Constants::DESCRIPTION) {
1387             tokenizer.assertToken(Constants::COLON);
1388             cfg->description = tokenizer.take();
1389         } else if (tok == Constants::PROMPTER) {
1390             tokenizer.assertToken(Constants::COLON);
1391             cfg->templateDescription = tokenizer.take();
1392         } else {
1393             throw ReadFailed(Constants::UNDEFINED_CONSTRUCT.arg(tok).arg(next));
1394         }
1395     }
1396 
1397     if (cfg->name.isEmpty()) {
1398         // Name is absent in old config files, ID was used as worker name.
1399         cfg->name = cfg->id;
1400     }
1401 
1402     return cfg;
1403 }
1404 
string2Actor(const QString & bytes)1405 ExternalProcessConfig *HRSchemaSerializer::string2Actor(const QString &bytes) {
1406     ExternalProcessConfig *cfg = nullptr;
1407     try {
1408         WorkflowSchemaReaderData data(bytes, nullptr, nullptr, nullptr);
1409         parseHeader(data.tokenizer, data.meta);
1410         cfg = parseActorBody(data.tokenizer);
1411     } catch (...) {
1412         return nullptr;
1413     }
1414     return cfg;
1415 }
1416 
addPart(QString & to,const QString & w)1417 void HRSchemaSerializer::addPart(QString &to, const QString &w) {
1418     QString what = w;
1419     if (!what.endsWith(Constants::NEW_LINE)) {
1420         what.append(Constants::NEW_LINE);
1421     }
1422     to += what + Constants::NEW_LINE;
1423 }
1424 
header2String(const Metadata * meta)1425 QString HRSchemaSerializer::header2String(const Metadata *meta) {
1426     QString res = Constants::HEADER_LINE + "\n";
1427     if (meta != nullptr) {
1428         QStringList descLines = meta->comment.split(Constants::NEW_LINE, QString::KeepEmptyParts);
1429         for (int lineIdx = 0; lineIdx < descLines.size(); lineIdx++) {
1430             const QString &line = descLines.at(lineIdx);
1431             bool lastLine = (lineIdx == descLines.size() - 1);
1432             if (lastLine && line.isEmpty()) {
1433                 continue;
1434             }
1435             res += Constants::SERVICE_SYM + line + Constants::NEW_LINE;
1436         }
1437     }
1438     return res;
1439 }
1440 
makeBlock(const QString & title,const QString & name,const QString & blockItself,int tabsNum,bool nl,bool sc)1441 QString HRSchemaSerializer::makeBlock(const QString &title, const QString &name, const QString &blockItself, int tabsNum, bool nl, bool sc) {
1442     QString indent = makeIndent(tabsNum);
1443     QString blockStart = Constants::BLOCK_START + Constants::NEW_LINE;
1444     if (nl) {
1445         blockStart += Constants::NEW_LINE;
1446     }
1447     QString blockEnd = Constants::BLOCK_END;
1448     if (sc) {
1449         blockEnd += Constants::SEMICOLON;
1450     }
1451     blockEnd += Constants::NEW_LINE;
1452     return indent + title + " " + valueString(name) + blockStart + blockItself + indent + blockEnd;
1453 }
1454 
makeEqualsPair(const QString & key,const QString & value,int tabsNum,bool quoteEmpty)1455 QString HRSchemaSerializer::makeEqualsPair(const QString &key, const QString &value, int tabsNum, bool quoteEmpty) {
1456     return makeIndent(tabsNum) + key + Constants::EQUALS_SIGN + valueString(value, quoteEmpty) + Constants::SEMICOLON + Constants::NEW_LINE;
1457 }
1458 
makeArrowPair(const QString & left,const QString & right,int tabsNum)1459 QString HRSchemaSerializer::makeArrowPair(const QString &left, const QString &right, int tabsNum) {
1460     return makeIndent(tabsNum) + left + Constants::DATAFLOW_SIGN + right;
1461 }
1462 
scriptBlock(const QString & scriptText,int tabsNum)1463 QString HRSchemaSerializer::scriptBlock(const QString &scriptText, int tabsNum) {
1464     QString indent = makeIndent(tabsNum);
1465     QString res;
1466     QStringList scriptLines = scriptText.split(Constants::NEW_LINE, QString::SkipEmptyParts);
1467     foreach (const QString &line, scriptLines) {
1468         res += indent + line + Constants::NEW_LINE;
1469     }
1470     return res;
1471 }
1472 
grouperOutSlotsDefinition(Attribute * attribute)1473 QString HRSchemaSerializer::grouperOutSlotsDefinition(Attribute *attribute) {
1474     GrouperOutSlotAttribute *a = dynamic_cast<GrouperOutSlotAttribute *>(attribute);
1475     QString result;
1476 
1477     foreach (const GrouperOutSlot &slot, a->getOutSlots()) {
1478         QString mRes;
1479         mRes += makeEqualsPair(Constants::NAME_ATTR, slot.getOutSlotId(), 3);
1480         mRes += makeEqualsPair(Constants::IN_SLOT, slot.getInSlotStr(), 3);
1481 
1482         GrouperSlotAction *const action = slot.getAction();
1483         if (nullptr != action) {
1484             QString actionBlock;
1485             actionBlock += makeEqualsPair(Constants::TYPE_ATTR, action->getType(), 4);
1486             foreach (const QString &paramId, action->getParameters().keys()) {
1487                 QVariant value = action->getParameterValue(paramId);
1488                 actionBlock += makeEqualsPair(paramId, value.toString(), 4);
1489             }
1490             mRes += makeBlock(Constants::ACTION, Constants::NO_NAME, actionBlock, 3);
1491         }
1492 
1493         result += makeBlock(Constants::OUT_SLOT_ATTR, Constants::NO_NAME, mRes, 2);
1494     }
1495 
1496     return result;
1497 }
1498 
1499 class HRUrlSerializer : public URLContainerVisitor {
1500 public:
HRUrlSerializer(int _tabCount)1501     HRUrlSerializer(int _tabCount)
1502         : tabCount(_tabCount) {
1503     }
1504 
visit(FileUrlContainer * url)1505     virtual void visit(FileUrlContainer *url) {
1506         result = HRSchemaSerializer::makeEqualsPair(Constants::FILE_URL, url->getUrl(), tabCount);
1507     }
1508 
visit(DirUrlContainer * url)1509     virtual void visit(DirUrlContainer *url) {
1510         if (url->getIncludeFilter().isEmpty() && url->getExcludeFilter().isEmpty() && !url->isRecursive()) {
1511             result = HRSchemaSerializer::makeEqualsPair(Constants::DIRECTORY_URL, url->getUrl(), tabCount);
1512             return;
1513         }
1514 
1515         QString res;
1516         res += HRSchemaSerializer::makeEqualsPair(Constants::PATH, url->getUrl(), tabCount + 1);
1517 
1518         processDirUrlContainerOptionalParams(url, res);
1519 
1520         result = HRSchemaSerializer::makeBlock(Constants::DIRECTORY_URL, Constants::NO_NAME, res, tabCount);
1521     }
1522 
visit(DbObjUrlContainer * url)1523     virtual void visit(DbObjUrlContainer *url) {
1524         const QString dbObjUrl = url->getUrl();
1525 
1526         QString res;
1527         res += HRSchemaSerializer::makeEqualsPair(Constants::DB_URL, SharedDbUrlUtils::getDbUrlFromEntityUrl(dbObjUrl), tabCount + 1);
1528         res += HRSchemaSerializer::makeEqualsPair(Constants::DB_OBJECT_ID, QString::number(SharedDbUrlUtils::getObjectNumberIdByUrl(dbObjUrl)), tabCount + 1);
1529         res += HRSchemaSerializer::makeEqualsPair(Constants::DB_OBJECT_TYPE, SharedDbUrlUtils::getDbSerializedObjectTypeByUrl(dbObjUrl), tabCount + 1);
1530         res += HRSchemaSerializer::makeEqualsPair(Constants::DB_OBJ_CACHED_NAME, SharedDbUrlUtils::getDbObjectNameByUrl(dbObjUrl), tabCount + 1);
1531 
1532         result = HRSchemaSerializer::makeBlock(Constants::DB_SELECT, Constants::NO_NAME, res, tabCount);
1533     }
1534 
visit(DbFolderUrlContainer * url)1535     virtual void visit(DbFolderUrlContainer *url) {
1536         const QString dbFolderUrl = url->getUrl();
1537 
1538         QString res;
1539         res += HRSchemaSerializer::makeEqualsPair(Constants::DB_URL, SharedDbUrlUtils::getDbUrlFromEntityUrl(dbFolderUrl), tabCount + 1);
1540         res += HRSchemaSerializer::makeEqualsPair(Constants::PATH, SharedDbUrlUtils::getDbFolderPathByUrl(dbFolderUrl), tabCount + 1);
1541         res += HRSchemaSerializer::makeEqualsPair(Constants::DB_OBJECT_TYPE, SharedDbUrlUtils::getDbFolderSerializedDataTypeByUrl(dbFolderUrl), tabCount + 1);
1542 
1543         processDbFolderUrlContainerOptionalParams(url, res);
1544 
1545         result = HRSchemaSerializer::makeBlock(Constants::DB_SELECT, Constants::NO_NAME, res, tabCount);
1546     }
1547 
getResult()1548     const QString &getResult() {
1549         return result;
1550     }
1551 
1552 private:
processDirUrlContainerOptionalParams(DirUrlContainer * url,QString & res)1553     void processDirUrlContainerOptionalParams(DirUrlContainer *url, QString &res) {
1554         const QString incFilter = url->getIncludeFilter();
1555         if (!incFilter.isEmpty()) {
1556             res += HRSchemaSerializer::makeEqualsPair(Constants::INC_FILTER, incFilter, tabCount + 1);
1557         }
1558 
1559         const QString excFilter = url->getExcludeFilter();
1560         if (!excFilter.isEmpty()) {
1561             res += HRSchemaSerializer::makeEqualsPair(Constants::EXC_FILTER, excFilter, tabCount + 1);
1562         }
1563 
1564         bool recursive = url->isRecursive();
1565         if (recursive) {
1566             const QString recStr = recursive ? BoolTypeValueFactory::TRUE_STR : BoolTypeValueFactory::FALSE_STR;
1567             res += HRSchemaSerializer::makeEqualsPair(Constants::RECURSIVE, recStr, tabCount + 1);
1568         }
1569     }
1570 
processDbFolderUrlContainerOptionalParams(DbFolderUrlContainer * url,QString & res)1571     void processDbFolderUrlContainerOptionalParams(DbFolderUrlContainer *url, QString &res) {
1572         bool recursive = url->isRecursive();
1573         if (recursive) {
1574             const QString recStr = recursive ? BoolTypeValueFactory::TRUE_STR : BoolTypeValueFactory::FALSE_STR;
1575             res += HRSchemaSerializer::makeEqualsPair(Constants::RECURSIVE, recStr, tabCount + 1);
1576         }
1577 
1578         const QString accFilter = url->getSequenceAccFilter();
1579         if (!accFilter.isEmpty()) {
1580             res += HRSchemaSerializer::makeEqualsPair(Constants::DB_SEQ_ACC_FILTER, accFilter, tabCount + 1);
1581         }
1582 
1583         const QString objNameFilter = url->getObjNameFilter();
1584         if (!objNameFilter.isEmpty()) {
1585             res += HRSchemaSerializer::makeEqualsPair(Constants::DB_OBJ_NAME_FILTER, objNameFilter, tabCount + 1);
1586         }
1587     }
1588 
1589     int tabCount;
1590     QString result;
1591 };
1592 
inUrlDefinitionBlocks(const QString & attrId,const QList<Dataset> & sets,int depth)1593 static QString inUrlDefinitionBlocks(const QString &attrId, const QList<Dataset> &sets, int depth) {
1594     QString res;
1595     foreach (const Dataset &dSet, sets) {
1596         QString setDef;
1597         setDef += HRSchemaSerializer::makeEqualsPair(Constants::DATASET_NAME, dSet.getName(), depth + 1);
1598         foreach (URLContainer *url, dSet.getUrls()) {
1599             HRUrlSerializer us(depth + 1);
1600             url->accept(&us);
1601             setDef += us.getResult();
1602         }
1603         res += HRSchemaSerializer::makeBlock(attrId,
1604                                              Constants::NO_NAME,
1605                                              setDef,
1606                                              depth);
1607     }
1608     return res;
1609 }
1610 
validatorDefinition(const ValidatorDesc & desc,int depth)1611 static QString validatorDefinition(const ValidatorDesc &desc, int depth) {
1612     QString result;
1613     QMap<QString, QString> options = desc.options;
1614     result += HRSchemaSerializer::makeEqualsPair(Constants::V_TYPE, desc.type, depth);
1615     if (Constants::V_SCRIPT == desc.type) {
1616         QString script = options.take(Constants::V_SCRIPT);
1617         result += HRSchemaSerializer::makeBlock(Constants::V_SCRIPT, Constants::NO_NAME, makeIndent(depth + 1) + script + Constants::NEW_LINE, depth, false, false);
1618     }
1619     foreach (const QString &key, options.keys()) {
1620         result += HRSchemaSerializer::makeEqualsPair(key, options[key], depth);
1621     }
1622     return result;
1623 }
1624 
elementsDefinitionBlock(Actor * actor,bool copyMode)1625 static QString elementsDefinitionBlock(Actor *actor, bool copyMode) {
1626     assert(actor != nullptr);
1627     QString res;
1628     // save global attributes
1629     res += HRSchemaSerializer::makeEqualsPair(Constants::TYPE_ATTR, actor->getProto()->getId());
1630     res += HRSchemaSerializer::makeEqualsPair(Constants::NAME_ATTR, actor->getLabel());
1631     if (copyMode) {
1632         res += HRSchemaSerializer::makeEqualsPair(Constants::ELEM_ID_ATTR, actor->getId());
1633     }
1634     AttributeScript *actorScript = actor->getScript();
1635     if (actorScript != nullptr && !actorScript->getScriptText().trimmed().isEmpty()) {
1636         res += HRSchemaSerializer::makeBlock(Constants::SCRIPT_ATTR, Constants::NO_NAME, actorScript->getScriptText() + Constants::NEW_LINE, 2, false, true);
1637     }
1638 
1639     // save local attributes
1640     foreach (Attribute *attribute, actor->getParameters().values()) {
1641         assert(attribute != nullptr);
1642         if (attribute->getGroup() == GROUPER_SLOT_GROUP) {
1643             res += HRSchemaSerializer::grouperOutSlotsDefinition(attribute);
1644         } else if (MARKER_GROUP == attribute->getGroup()) {
1645             res += HRSchemaSerializer::markersDefinition(attribute);
1646         } else {
1647             if (attribute->getAttributeType() == BaseTypes::URL_DATASETS_TYPE()) {
1648                 QVariant v = attribute->getAttributePureValue();
1649                 if (v.canConvert<QList<Dataset>>()) {
1650                     QList<Dataset> sets = v.value<QList<Dataset>>();
1651                     res += inUrlDefinitionBlocks(attribute->getId(), sets, 2);
1652                     continue;
1653                 }
1654             }
1655             QString attributeId = attribute->getId();
1656             assert(!attributeId.contains(QRegExp("\\s")));
1657 
1658             const AttributeScript &attrScript = attribute->getAttributeScript();
1659             if (!attrScript.isEmpty()) {
1660                 res += HRSchemaSerializer::makeBlock(attributeId, Constants::NO_NAME, attrScript.getScriptText() + Constants::NEW_LINE, 2, false, true);
1661                 continue;
1662             }
1663 
1664             if (attribute->isDefaultValue()) {
1665                 continue;
1666             }
1667             QVariant value = attribute->getAttributePureValue();
1668 
1669 #ifdef _DEBUG
1670             const bool valueIsNull = value.isNull();
1671             const bool valueCanConvertToString = value.canConvert<QString>();
1672             const bool valueCanConvertToStringList = value.canConvert<QStringList>();
1673             const bool valueCanConvertToMap = value.canConvert<QMap<QString, QVariant>>();
1674             assert(valueIsNull ||
1675                    valueCanConvertToString ||
1676                    valueCanConvertToStringList ||
1677                    valueCanConvertToMap);
1678 #endif
1679 
1680             QString valueString;
1681             if (attribute->getAttributeType() == BaseTypes::STRING_LIST_TYPE()) {
1682                 valueString = StrPackUtils::packStringList(value.toStringList(), StrPackUtils::SingleQuotes);
1683             } else if (attribute->getAttributeType() == BaseTypes::MAP_TYPE()) {
1684                 valueString = StrPackUtils::packMap(value.toMap(), StrPackUtils::SingleQuotes);
1685             } else {
1686                 valueString = value.toString();
1687             }
1688             res += HRSchemaSerializer::makeEqualsPair(attributeId, valueString, 2, true);
1689         }
1690     }
1691 
1692     foreach (const ValidatorDesc &desc, actor->getCustomValidators()) {
1693         res += HRSchemaSerializer::makeBlock(Constants::VALIDATOR, Constants::NO_NAME, validatorDefinition(desc, 3), 2, false, false);
1694     }
1695 
1696     return res;
1697 }
1698 
tryGetRelativePath(const QString & path)1699 static QString tryGetRelativePath(const QString &path) {
1700     QString dir;
1701 
1702     if (path.startsWith(WorkflowSettings::getExternalToolDirectory())) {
1703         dir = WorkflowSettings::getExternalToolDirectory();
1704     } else if (path.startsWith(WorkflowSettings::getUserDirectory())) {
1705         dir = WorkflowSettings::getUserDirectory();
1706     } else if (path.startsWith(WorkflowSettings::getIncludedElementsDirectory())) {
1707         dir = WorkflowSettings::getIncludedElementsDirectory();
1708     }
1709 
1710     if (dir.isEmpty()) {
1711         return path;
1712     } else {
1713         return path.mid(dir.length());
1714     }
1715 }
1716 
includesDefinition(const QList<Actor * > & procs)1717 QString HRSchemaSerializer::includesDefinition(const QList<Actor *> &procs) {
1718     QString res;
1719     foreach (Actor *proc, procs) {
1720         ActorPrototype *proto = proc->getProto();
1721         if (!proto->isStandardFlagSet()) {
1722             res += Constants::INCLUDE + " \"" + tryGetRelativePath(proto->getFilePath()) + "\" ";
1723             res += Constants::INCLUDE_AS + " \"" + proto->getId() + "\"" + Constants::NEW_LINE;
1724         }
1725     }
1726 
1727     return res;
1728 }
1729 
elementsDefinition(const QList<Actor * > & procs,const NamesMap & nmap,bool copyMode)1730 QString HRSchemaSerializer::elementsDefinition(const QList<Actor *> &procs, const NamesMap &nmap, bool copyMode) {
1731     QString res;
1732     foreach (Actor *actor, procs) {
1733         QString idStr = nmap[actor->getId()];
1734         SAFE_POINT(!idStr.contains(QRegExp("\\s")), tr("Error: element name in the workflow file contains spaces"), QString());
1735         res += makeBlock(idStr, Constants::NO_NAME, elementsDefinitionBlock(actor, copyMode));
1736     }
1737     return res + Constants::NEW_LINE;
1738 }
1739 
markerDefinitionBlock(Marker * marker,int tabsNum)1740 static QString markerDefinitionBlock(Marker *marker, int tabsNum) {
1741     assert(marker != nullptr);
1742     QString res;
1743     res += HRSchemaSerializer::makeEqualsPair(Constants::TYPE_ATTR, marker->getType(), tabsNum);
1744     res += HRSchemaSerializer::makeEqualsPair(Constants::NAME_ATTR, marker->getName(), tabsNum);
1745 
1746     if (QUALIFIER == marker->getGroup()) {
1747         const QString &qualName = dynamic_cast<QualifierMarker *>(marker)->getQualifierName();
1748         if (!qualName.isEmpty()) {
1749             res += HRSchemaSerializer::makeEqualsPair(Constants::QUAL_NAME, qualName, tabsNum);
1750         }
1751     } else if (ANNOTATION == marker->getGroup()) {
1752         const QString &annName = dynamic_cast<AnnotationMarker *>(marker)->getAnnotationName();
1753         if (!annName.isEmpty()) {
1754             res += HRSchemaSerializer::makeEqualsPair(Constants::ANN_NAME, annName, tabsNum);
1755         }
1756     }
1757 
1758     foreach (QString key, marker->getValues().keys()) {
1759         QString val = marker->getValues().value(key);
1760         res += HRSchemaSerializer::makeEqualsPair("\"" + key + "\"", val, tabsNum);
1761     }
1762     return res;
1763 }
1764 
actorBindingsBlock(const ActorBindingsGraph & graph,const HRSchemaSerializer::NamesMap & nmap,bool)1765 static QString actorBindingsBlock(const ActorBindingsGraph &graph, const HRSchemaSerializer::NamesMap &nmap, bool) {
1766     QString res;
1767 
1768     foreach (Port *srcPort, graph.getBindings().keys()) {
1769         QString srcActorId = nmap[srcPort->owner()->getId()];
1770         QString srcPortId = srcPort->getId();
1771         foreach (Port *dstPort, graph.getBindings().value(srcPort)) {
1772             QString dstActorId = nmap[dstPort->owner()->getId()];
1773             QString dstPortId = dstPort->getId();
1774 
1775             res += HRSchemaSerializer::makeArrowPair(srcActorId + Constants::DOT + srcPortId,
1776                                                      dstActorId + Constants::DOT + dstPortId,
1777                                                      2) +
1778                    Constants::NEW_LINE;
1779         }
1780     }
1781     return res;
1782 }
1783 
actorBindings(const ActorBindingsGraph & graph,const NamesMap & nmap,bool copyMode)1784 QString HRSchemaSerializer::actorBindings(const ActorBindingsGraph &graph, const NamesMap &nmap, bool copyMode) {
1785     QString res;
1786     res += makeBlock(Constants::ACTOR_BINDINGS, Constants::NO_NAME, actorBindingsBlock(graph, nmap, copyMode));
1787     return res + Constants::NEW_LINE;
1788 }
1789 
containsProcWithId(const QList<Actor * > & procs,const ActorId & id)1790 static bool containsProcWithId(const QList<Actor *> &procs, const ActorId &id) {
1791     foreach (Actor *a, procs) {
1792         if (a->getId() == id) {
1793             return true;
1794         }
1795     }
1796     return false;
1797 }
1798 
dataflowDefinition(const QList<Actor * > & procs,const NamesMap & nmap)1799 QString HRSchemaSerializer::dataflowDefinition(const QList<Actor *> &procs, const NamesMap &nmap) {
1800     QString res;
1801     foreach (Actor *actor, procs) {
1802         foreach (Port *inputPort, actor->getEnabledInputPorts()) {
1803             StrStrMap busMap = inputPort->getParameter(IntegralBusPort::BUS_MAP_ATTR_ID)->getAttributeValueWithoutScript<StrStrMap>();
1804             IntegralBusPort *busPort = qobject_cast<IntegralBusPort *>(inputPort);
1805 
1806             foreach (const QString &key, busMap.keys()) {
1807                 QStringList srcList = busMap.value(key).split(";", QString::SkipEmptyParts);
1808                 QStringList uniqList;
1809                 foreach (QString src, srcList) {
1810                     if (!uniqList.contains(src)) {
1811                         uniqList << src;
1812                     }
1813                 }
1814 
1815                 foreach (QString src, uniqList) {
1816                     if (src.isEmpty()) {
1817                         continue;
1818                     }
1819                     QList<QStringList> paths = busPort->getPathsBySlotsPair(key, src);
1820                     src = src.replace(Constants::COLON, Constants::DOT);
1821                     ActorId srcActorId = parseAt(src, 0);
1822 
1823                     if (containsProcWithId(procs, srcActorId)) {
1824                         QString arrowPair = makeArrowPair(src.replace(srcActorId, nmap[srcActorId]),
1825                                                           nmap[actor->getId()] + Constants::DOT + inputPort->getId() + Constants::DOT + key,
1826                                                           0);
1827 
1828                         if (paths.isEmpty()) {
1829                             res += makeIndent(1) + arrowPair + Constants::NEW_LINE;
1830                         } else {
1831                             foreach (const QStringList &path, paths) {
1832                                 QString pathString = path.join(", ");
1833                                 QString pair = makeEqualsPair(Constants::PATH_THROUGH, pathString, 2);
1834                                 res += makeBlock(arrowPair, Constants::NO_NAME, pair);
1835                             }
1836                         }
1837                     }
1838                 }
1839             }
1840         }
1841     }
1842     return res + Constants::NEW_LINE;
1843 }
1844 
visualData(const Schema & schema,const HRSchemaSerializer::NamesMap & nmap)1845 static QString visualData(const Schema &schema, const HRSchemaSerializer::NamesMap &nmap) {
1846     QString res;
1847     foreach (Link *link, schema.getFlows()) {
1848         Port *src = link->source();
1849         Port *dst = link->destination();
1850         res += HRSchemaSerializer::makeArrowPair(nmap[src->owner()->getId()] + Constants::DOT + src->getId(),
1851                                                  nmap[dst->owner()->getId()] + Constants::DOT + dst->getId(),
1852                                                  0) +
1853                Constants::NEW_LINE;
1854     }
1855     return res;
1856 }
1857 
itemsMetaData(const QList<Actor * > & actors,const Metadata * meta,const HRSchemaSerializer::NamesMap & nmap)1858 static QString itemsMetaData(const QList<Actor *> &actors, const Metadata *meta, const HRSchemaSerializer::NamesMap &nmap) {
1859     QString res;
1860     bool hasParameterAliases = false;
1861     foreach (Actor *a, actors) {
1862         if (a->hasParamAliases()) {
1863             hasParameterAliases = true;
1864             break;
1865         }
1866     }
1867     if (hasParameterAliases) {
1868         res += HRSchemaSerializer::makeBlock(Constants::PARAM_ALIASES_START, Constants::NO_NAME, HRSchemaSerializer::schemaParameterAliases(actors, nmap), 2);
1869     }
1870 
1871     if (nullptr != meta) {
1872         HRVisualSerializer vs(*meta, nmap);
1873         res += vs.serialize(2);
1874     }
1875     return res;
1876 }
1877 
metaData(const Schema & schema,const Metadata * meta,const HRSchemaSerializer::NamesMap & nmap)1878 static QString metaData(const Schema &schema, const Metadata *meta, const HRSchemaSerializer::NamesMap &nmap) {
1879     QString res;
1880 
1881     res += itemsMetaData(schema.getProcesses(), meta, nmap);
1882 
1883     if (nullptr != meta && !meta->estimationsCode.isEmpty()) {
1884         res += HRSchemaSerializer::makeBlock(Constants::ESTIMATIONS, Constants::NO_NAME, meta->estimationsCode + Constants::NEW_LINE, 2);
1885     }
1886 
1887     if (schema.hasPortAliases()) {
1888         res += HRSchemaSerializer::makeBlock(Constants::PORT_ALIASES_START, Constants::NO_NAME, HRSchemaSerializer::schemaPortAliases(nmap, schema.getPortAliases()), 2);
1889     }
1890 
1891     if (nullptr == meta) {
1892         res += HRSchemaSerializer::makeBlock(Constants::VISUAL_START, Constants::NO_NAME, visualData(schema, nmap), 2);
1893     }
1894 
1895     foreach (Wizard *w, schema.getWizards()) {
1896         HRWizardSerializer ws;
1897         res += ws.serialize(w, 2);
1898     }
1899     return res;
1900 }
1901 
schemaParameterAliases(const QList<Actor * > & procs,const NamesMap & nmap)1902 QString HRSchemaSerializer::schemaParameterAliases(const QList<Actor *> &procs, const NamesMap &nmap) {
1903     QString res;
1904     foreach (Actor *actor, procs) {
1905         const QMap<QString, QString> &aliases = actor->getParamAliases();
1906         foreach (const QString &attrId, aliases.uniqueKeys()) {
1907             QString pairs;
1908             QString alias = aliases.value(attrId);
1909             QString descr = actor->getAliasHelp()[alias];
1910             pairs += HRSchemaSerializer::makeEqualsPair(Constants::ALIAS, alias, 4);
1911             if (!descr.isEmpty()) {
1912                 pairs += HRSchemaSerializer::makeEqualsPair(Constants::DESCRIPTION, descr, 4);
1913             }
1914             QString paramString = nmap[actor->getId()] + Constants::DOT + attrId;
1915             res += makeBlock(paramString, Constants::NO_NAME, pairs, 3);
1916         }
1917     }
1918     return res;
1919 }
1920 
schemaPortAliases(const NamesMap & nmap,const QList<PortAlias> & portAliases)1921 QString HRSchemaSerializer::schemaPortAliases(const NamesMap &nmap, const QList<PortAlias> &portAliases) {
1922     QString res;
1923 
1924     foreach (const PortAlias &portAlias, portAliases) {
1925         QString pairs;
1926         pairs += makeEqualsPair(Constants::ALIAS, portAlias.getAlias(), 4);
1927         if (!portAlias.getDescription().isEmpty()) {
1928             pairs += makeEqualsPair(Constants::DESCRIPTION, portAlias.getDescription(), 4);
1929         }
1930         foreach (const SlotAlias &slotAlias, portAlias.getSlotAliases()) {
1931             QString actorName = nmap[slotAlias.getSourcePort()->owner()->getId()];
1932             QString portId = slotAlias.getSourcePort()->getId();
1933             QString slotString = actorName + Constants::DOT + portId + Constants::DOT + slotAlias.getSourceSlotId();
1934             pairs += makeEqualsPair(slotString, slotAlias.getAlias(), 4);
1935         }
1936 
1937         const Port *sourcePort = portAlias.getSourcePort();
1938         SAFE_POINT(sourcePort != nullptr, "sourcePort is nullptr", QString());
1939 
1940         QString sourceActorName = nmap[sourcePort->owner()->getId()];
1941         QString sourcePortId = sourcePort->getId();
1942         QString portString = sourceActorName + Constants::DOT + sourcePortId;
1943         res += makeBlock(portString, Constants::NO_NAME, pairs, 3);
1944     }
1945 
1946     return res;
1947 }
1948 
generateElementNames(const QList<Actor * > & procs)1949 HRSchemaSerializer::NamesMap HRSchemaSerializer::generateElementNames(const QList<Actor *> &procs) {
1950     QMap<ActorId, QString> nmap;
1951     foreach (Actor *proc, procs) {
1952         QString id = aid2str(proc->getId());
1953         QString name = id.replace(QRegExp("\\s"), "-");
1954         nmap[proc->getId()] = name;  // generateElementName(proc, nmap.values());
1955     }
1956     return nmap;
1957 }
1958 
bodyItself(const Schema & schema,const Metadata * meta,bool copyMode)1959 static QString bodyItself(const Schema &schema, const Metadata *meta, bool copyMode) {
1960     HRSchemaSerializer::NamesMap nmap = HRSchemaSerializer::generateElementNames(schema.getProcesses());
1961     QString res;
1962     res += HRSchemaSerializer::elementsDefinition(schema.getProcesses(), nmap, copyMode);
1963     res += HRSchemaSerializer::actorBindings(schema.getActorBindingsGraph(), nmap, copyMode);
1964     res += HRSchemaSerializer::dataflowDefinition(schema.getProcesses(), nmap);
1965     res += HRSchemaSerializer::makeBlock(Constants::META_START, schema.getTypeName(), metaData(schema, meta, nmap));
1966     return res;
1967 }
1968 
schema2String(const Schema & schema,const Metadata * meta,bool copyMode)1969 QString HRSchemaSerializer::schema2String(const Schema &schema, const Metadata *meta, bool copyMode) {
1970     QString res;
1971     addPart(res, header2String(meta));
1972     addPart(res, includesDefinition(schema.getProcesses()));
1973     addPart(res, makeBlock(Constants::BODY_START, meta ? meta->name : "", bodyItself(schema, meta, copyMode), 0, true));
1974     return res;
1975 }
1976 
items2String(const QList<Actor * > & actors,const Metadata * meta)1977 QString HRSchemaSerializer::items2String(const QList<Actor *> &actors, const Metadata *meta) {
1978     assert(!actors.isEmpty());
1979     QString res;
1980     HRSchemaSerializer::addPart(res, HRSchemaSerializer::header2String(meta));
1981 
1982     QString iData;
1983     HRSchemaSerializer::NamesMap nmap = HRSchemaSerializer::generateElementNames(actors);
1984     iData += HRSchemaSerializer::elementsDefinition(actors, nmap);
1985     iData += HRSchemaSerializer::dataflowDefinition(actors, nmap);
1986     iData += HRSchemaSerializer::makeBlock(Constants::META_START, Constants::NO_NAME, itemsMetaData(actors, meta, nmap));
1987 
1988     HRSchemaSerializer::addPart(res, HRSchemaSerializer::makeBlock(Constants::BODY_START, Constants::NO_NAME, iData, 0, true));
1989     return res;
1990 }
1991 
deepCopy(const Schema & from,Schema * to,U2OpStatus & os)1992 QMap<ActorId, ActorId> HRSchemaSerializer::deepCopy(const Schema &from, Schema *to, U2OpStatus &os) {
1993     assert(to != nullptr);
1994     QString data = schema2String(from, nullptr, true);
1995     QMap<ActorId, ActorId> idMap;
1996     QString err = string2Schema(data, to, nullptr, &idMap);
1997     if (!err.isEmpty()) {
1998         os.setError(err);
1999         coreLog.details(err);
2000         to->reset();
2001         return QMap<ActorId, ActorId>();
2002     }
2003     to->setDeepCopyFlag(true);
2004     return idMap;
2005 }
2006 
inputsDefenition(const QList<DataConfig> & inputs)2007 static QString inputsDefenition(const QList<DataConfig> &inputs) {
2008     QString res = Constants::TAB + Constants::INPUT_START + " {\n";
2009     foreach (const DataConfig &cfg, inputs) {
2010         res += Constants::TAB + Constants::TAB + cfg.attributeId + " {\n";
2011         res += Constants::TAB + Constants::TAB + Constants::TAB + "name:\"" + cfg.attrName + "\";\n";
2012         res += Constants::TAB + Constants::TAB + Constants::TAB + "type:" + cfg.type + ";\n";
2013         res += Constants::TAB + Constants::TAB + Constants::TAB + "format:" + cfg.format + ";\n";
2014         if (!cfg.description.isEmpty()) {
2015             res += Constants::TAB + Constants::TAB + Constants::TAB + "description:\"" + cfg.description + "\";\n";
2016         }
2017         res += Constants::TAB + Constants::TAB + "}\n";
2018     }
2019     res += Constants::TAB + "}\n";
2020     return res;
2021 }
2022 
outputsDefenition(const QList<DataConfig> & inputs)2023 static QString outputsDefenition(const QList<DataConfig> &inputs) {
2024     QString res = Constants::TAB + Constants::OUTPUT_START + " {\n";
2025     foreach (const DataConfig &cfg, inputs) {
2026         res += Constants::TAB + Constants::TAB + cfg.attributeId + " {\n";
2027         res += Constants::TAB + Constants::TAB + Constants::TAB + "name:\"" + cfg.attrName + "\";\n";
2028         res += Constants::TAB + Constants::TAB + Constants::TAB + "type:" + cfg.type + ";\n";
2029         res += Constants::TAB + Constants::TAB + Constants::TAB + "format:" + cfg.format + ";\n";
2030         if (!cfg.description.isEmpty()) {
2031             res += Constants::TAB + Constants::TAB + Constants::TAB + "description:\"" + cfg.description + "\";\n";
2032         }
2033         res += Constants::TAB + Constants::TAB + "}\n";
2034     }
2035     res += Constants::TAB + "}\n";
2036     return res;
2037 }
2038 
2039 namespace {
2040 
bool2String(bool value)2041 QString bool2String(bool value) {
2042     return value ? Constants::TRUE : Constants::FALSE;
2043 }
2044 
2045 }  // namespace
2046 
attributesDefinition(const QList<AttributeConfig> & attrs)2047 static QString attributesDefinition(const QList<AttributeConfig> &attrs) {
2048     QString res = Constants::TAB + Constants::ATTRIBUTES_START + " {\n";
2049     foreach (const AttributeConfig &cfg, attrs) {
2050         res += Constants::TAB + Constants::TAB + cfg.attributeId + " {\n";
2051         res += Constants::TAB + Constants::TAB + Constants::TAB + "name:\"" + cfg.attrName + "\";\n";
2052         res += Constants::TAB + Constants::TAB + Constants::TAB + "type:" + cfg.type + ";\n";
2053         if (!cfg.defaultValue.isEmpty()) {
2054             res += Constants::TAB + Constants::TAB + Constants::TAB + Constants::DEFAULT_VALUE + ":\"" + cfg.defaultValue + "\";\n";
2055         }
2056         if (!cfg.description.isEmpty()) {
2057             res += Constants::TAB + Constants::TAB + Constants::TAB + Constants::DESCRIPTION + Constants::COLON + Constants::QUOTE + cfg.description + Constants::QUOTE + Constants::SEMICOLON + Constants::NEW_LINE;
2058         }
2059         if (cfg.isOutputUrl()) {
2060             res += Constants::TAB + Constants::TAB + Constants::TAB + Constants::ADD_TO_DASHBOARD + Constants::COLON + bool2String(cfg.flags.testFlag(AttributeConfig::AddToDashboard)) + Constants::SEMICOLON + Constants::NEW_LINE;
2061             if (cfg.isFile()) {
2062                 res += Constants::TAB + Constants::TAB + Constants::TAB + Constants::OPEN_WITH_UGENE + Constants::COLON + bool2String(cfg.flags.testFlag(AttributeConfig::OpenWithUgene)) + Constants::SEMICOLON + Constants::NEW_LINE;
2063             }
2064         }
2065         res += Constants::TAB + Constants::TAB + "}\n";
2066     }
2067     res += Constants::TAB + "}\n";
2068     return res;
2069 }
2070 
actor2String(ExternalProcessConfig * cfg)2071 QString HRSchemaSerializer::actor2String(ExternalProcessConfig *cfg) {
2072     QString res = Constants::HEADER_LINE + "\n";
2073     res += "\"" + cfg->id + "\" {\n";
2074     res += inputsDefenition(cfg->inputs);
2075     res += outputsDefenition(cfg->outputs);
2076     res += attributesDefinition(cfg->attrs);
2077     res += Constants::TAB + Constants::NAME_ATTR + ":\"" + cfg->name + "\";\n";
2078     res += Constants::TAB + Constants::USE_INTEGRATED_TOOL + ":" + (cfg->useIntegratedTool ? Constants::TRUE : Constants::FALSE) + ";\n";
2079     if (!cfg->customToolPath.isEmpty()) {
2080         res += Constants::TAB + Constants::CUSTOM_TOOL_PATH + ":\"" + cfg->customToolPath + "\";\n";  // TODO: it should be escaped (UGENE-6437)
2081     }
2082     if (!cfg->integratedToolId.isEmpty()) {
2083         res += Constants::TAB + Constants::INTEGRATED_TOOL_ID + ":\"" + cfg->integratedToolId + "\";\n";  // TODO: it also should be escaped (UGENE-6437)
2084     }
2085     res += Constants::TAB + Constants::CMDLINE + ":\"" + cfg->cmdLine + "\";\n";
2086     if (!cfg->description.isEmpty()) {
2087         res += Constants::TAB + Constants::DESCRIPTION + ":\"" + cfg->description + "\";\n";
2088     }
2089     if (!cfg->templateDescription.isEmpty()) {
2090         res += Constants::TAB + Constants::PROMPTER + ":\"" + cfg->templateDescription + "\";\n";
2091     }
2092     res += "}";
2093     return res;
2094 }
2095 
deprecatedActorsReplacer(const QString & id,const QString & protoId,ParsedPairs & pairs)2096 Actor *HRSchemaSerializer::deprecatedActorsReplacer(const QString &id, const QString &protoId, ParsedPairs &pairs) {
2097     Actor *a = nullptr;
2098     ActorPrototype *apt = nullptr;
2099     if (protoId == CoreLibConstants::WRITE_CLUSTAL_PROTO_ID) {
2100         apt = WorkflowEnv::getProtoRegistry()->getProto(SchemaSerializer::getElemType(CoreLibConstants::WRITE_MSA_PROTO_ID));
2101         a = apt->createInstance(id);
2102         a->setParameter(BaseAttributes::DOCUMENT_FORMAT_ATTRIBUTE().getId(), BaseDocumentFormats::CLUSTAL_ALN);
2103         pairs.blockPairs.remove("accumulate");
2104         pairs.equalPairs.remove("accumulate");
2105     }
2106     if (protoId == CoreLibConstants::WRITE_STOCKHOLM_PROTO_ID) {
2107         apt = WorkflowEnv::getProtoRegistry()->getProto(SchemaSerializer::getElemType(CoreLibConstants::WRITE_MSA_PROTO_ID));
2108         a = apt->createInstance(id);
2109         a->setParameter(BaseAttributes::DOCUMENT_FORMAT_ATTRIBUTE().getId(), BaseDocumentFormats::STOCKHOLM);
2110         pairs.blockPairs.remove("accumulate");
2111         pairs.equalPairs.remove("accumulate");
2112     }
2113     if (protoId == CoreLibConstants::WRITE_FASTQ_PROTO_ID) {
2114         apt = WorkflowEnv::getProtoRegistry()->getProto(SchemaSerializer::getElemType(CoreLibConstants::WRITE_SEQ_PROTO_ID));
2115         a = apt->createInstance(id);
2116         a->setParameter(BaseAttributes::DOCUMENT_FORMAT_ATTRIBUTE().getId(), BaseDocumentFormats::FASTQ);
2117     }
2118     if (protoId == CoreLibConstants::WRITE_GENBANK_PROTO_ID) {
2119         apt = WorkflowEnv::getProtoRegistry()->getProto(SchemaSerializer::getElemType(CoreLibConstants::WRITE_SEQ_PROTO_ID));
2120         a = apt->createInstance(id);
2121         a->setParameter(BaseAttributes::DOCUMENT_FORMAT_ATTRIBUTE().getId(), BaseDocumentFormats::PLAIN_GENBANK);
2122     }
2123     return a;
2124 }
2125 
parseMarkers(Actor * proc,const QStringList & markerDefs,const QString & attrId)2126 void HRSchemaSerializer::parseMarkers(Actor *proc, const QStringList &markerDefs, const QString &attrId) {
2127     MarkerAttribute *attr = dynamic_cast<MarkerAttribute *>(proc->getParameter(attrId));
2128     if (nullptr == attr) {
2129         throw ReadFailed(tr("%1 actor has not marker attribute").arg(proc->getId()));
2130     }
2131 
2132     SAFE_POINT(1 == proc->getEnabledOutputPorts().size(), "Wrong out ports count", );
2133     Port *outPort = proc->getEnabledOutputPorts().first();
2134     QMap<Descriptor, DataTypePtr> outTypeMap = outPort->getOutputType()->getDatatypesMap();
2135 
2136     foreach (const QString &def, markerDefs) {
2137         Marker *marker = parseMarker(def);
2138         SAFE_POINT_EXT(nullptr != marker, throw ReadFailed("NULL marker"), );
2139 
2140         Descriptor newSlot = MarkerSlots::getSlotByMarkerType(marker->getType(), marker->getName());
2141         outTypeMap[newSlot] = BaseTypes::STRING_TYPE();
2142         attr->getMarkers() << marker;
2143     }
2144 
2145     DataTypePtr newType(new MapDataType(dynamic_cast<Descriptor &>(*(outPort->getType())), outTypeMap));
2146     outPort->setNewType(newType);
2147 }
2148 
parseMarker(ParsedPairs & pairs,const QString & MARKER_TYPE,const QString & MARKER_NAME)2149 Marker *HRSchemaSerializer::parseMarker(ParsedPairs &pairs, const QString &MARKER_TYPE, const QString &MARKER_NAME) {
2150     const QString markerType = pairs.equalPairs.take(MARKER_TYPE);
2151     const QString markerName = pairs.equalPairs.take(MARKER_NAME);
2152     if (markerName.isEmpty()) {
2153         throw ReadFailed(tr("Name attribute is not set for the marker"));
2154     }
2155     if (markerType.isEmpty()) {
2156         throw ReadFailed(tr("Type attribute is not set for %1 marker").arg(markerName));
2157     }
2158 
2159     Marker *marker = nullptr;
2160     if (markerType == MarkerTypes::QUAL_INT_VALUE_MARKER_ID || markerType == MarkerTypes::QUAL_TEXT_VALUE_MARKER_ID || markerType == MarkerTypes::QUAL_FLOAT_VALUE_MARKER_ID) {
2161         const QString qualName = pairs.equalPairs.take(Constants::QUAL_NAME);
2162         if (qualName.isEmpty()) {
2163             throw ReadFailed(tr("Qualifier name attribute is not set for %1 marker").arg(markerName));
2164         }
2165         marker = new QualifierMarker(markerType, markerName, qualName);
2166     } else if (MarkerTypes::ANNOTATION_LENGTH_MARKER_ID == markerType || MarkerTypes::ANNOTATION_COUNT_MARKER_ID == markerType) {
2167         QString annName = pairs.equalPairs.take(Constants::ANN_NAME);
2168         marker = new AnnotationMarker(markerType, markerName, annName);
2169     } else if (MarkerTypes::TEXT_MARKER_ID == markerType) {
2170         marker = new TextMarker(markerType, markerName);
2171     } else {
2172         marker = new SequenceMarker(markerType, markerName);
2173     }
2174     foreach (const QString &key, pairs.equalPairs.keys()) {
2175         marker->addValue(key, pairs.equalPairs.value(key));
2176     }
2177 
2178     return marker;
2179 }
2180 
parseMarker(const QString & def)2181 Marker *HRSchemaSerializer::parseMarker(const QString &def) {
2182     ParsedPairs pairs(def);
2183     return parseMarker(pairs, Constants::TYPE_ATTR, Constants::NAME_ATTR);
2184 }
2185 
markersDefinition(Attribute * attribute)2186 QString HRSchemaSerializer::markersDefinition(Attribute *attribute) {
2187     MarkerAttribute *mAttr = dynamic_cast<MarkerAttribute *>(attribute);
2188     SAFE_POINT(nullptr != mAttr, "NULL marker attribute", "");
2189     QString res;
2190 
2191     foreach (Marker *marker, mAttr->getMarkers()) {
2192         res += makeBlock(attribute->getId(), Constants::NO_NAME, markerDefinitionBlock(marker, 3), 2);
2193     }
2194     return res + Constants::NEW_LINE;
2195 }
2196 
2197 }  // namespace U2
2198