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 ¶mId, 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 ¶mString, const QMap<QString, Actor *> &actorMap, QString &actorName, QString ¶mId) {
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 ¶mId, 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