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 "Actor.h"
23
24 #include <U2Core/L10n.h>
25 #include <U2Core/U2OpStatusUtils.h>
26 #include <U2Core/U2SafePoints.h>
27
28 #include <U2Lang/ActorPrototype.h>
29 #include <U2Lang/BaseTypes.h>
30 #include <U2Lang/CoreLibConstants.h>
31 #include <U2Lang/GrouperOutSlot.h>
32 #include <U2Lang/GrouperSlotAttribute.h>
33 #include <U2Lang/WorkflowEnv.h>
34 #include <U2Lang/WorkflowUtils.h>
35
36 #include "ActorDocument.h"
37 #include "support/IntegralBusUtils.h"
38
39 namespace U2 {
40 namespace Workflow {
41
Actor(const ActorId & actorId,ActorPrototype * proto,AttributeScript * _script)42 Actor::Actor(const ActorId &actorId, ActorPrototype *proto, AttributeScript *_script)
43 : id(actorId), proto(proto), doc(nullptr), script(_script), condition(new AttributeScript()) {
44 if (script == nullptr) {
45 if (proto->isScriptFlagSet()) {
46 script = new AttributeScript();
47 script->setScriptText("");
48 } else {
49 script = nullptr;
50 }
51 }
52
53 if (script != nullptr) {
54 setupVariablesForPort(script);
55 setupVariablesForAttribute(script);
56 }
57 setupVariablesForPort(condition, true);
58 setupVariablesForAttribute(condition);
59
60 connect(proto, SIGNAL(si_nameChanged()), SLOT(sl_labelChanged()));
61 connect(proto, SIGNAL(si_descriptionChanged()), SIGNAL(si_descriptionChanged()));
62 }
63
Actor(const Actor &)64 Actor::Actor(const Actor &)
65 : QObject(), Configuration(), Peer() {
66 assert(false);
67 }
68
~Actor()69 Actor::~Actor() {
70 qDeleteAll(ports.values());
71 delete doc;
72 delete script;
73 delete condition;
74 }
75
sl_labelChanged()76 void Actor::sl_labelChanged() {
77 setLabel(proto->getDisplayName());
78 }
79
setupVariablesForPort(AttributeScript * _script,bool inputOnly)80 void Actor::setupVariablesForPort(AttributeScript *_script, bool inputOnly) {
81 QList<PortDescriptor *> portDesciptors = proto->getPortDesciptors();
82 for (const PortDescriptor *descr : qAsConst(portDesciptors)) {
83 QString prefix;
84 if (descr->isInput()) {
85 prefix = "in_";
86 } else if (!inputOnly) {
87 prefix = "out_";
88 } else {
89 continue;
90 }
91
92 DataTypePtr dataTypePtr = descr->getType();
93 if (dataTypePtr->isMap()) {
94 QMap<Descriptor, DataTypePtr> map = dataTypePtr->getDatatypesMap();
95 foreach (const Descriptor &d, map.keys()) {
96 Descriptor var(prefix + d.getId(), d.getDisplayName(), d.getDocumentation());
97 _script->setScriptVar(var, QVariant());
98 }
99 } else if (dataTypePtr->isList()) {
100 foreach (const Descriptor &typeDescr, dataTypePtr->getAllDescriptors()) {
101 Descriptor var(prefix + typeDescr.getId(), typeDescr.getDisplayName(), typeDescr.getDocumentation());
102 _script->setScriptVar(var, QVariant());
103 }
104 } else {
105 QString id = prefix + dataTypePtr->getId();
106 QString displayName = dataTypePtr->getDisplayName();
107 QString doc = prefix + dataTypePtr->getDocumentation();
108 _script->setScriptVar(Descriptor(id, displayName, doc), QVariant());
109 }
110 }
111 }
112
setupVariablesForAttribute(AttributeScript * _script)113 void Actor::setupVariablesForAttribute(AttributeScript *_script) {
114 foreach (Attribute *attribute, proto->getAttributes()) {
115 assert(attribute != nullptr);
116 QString attrVarName = attribute->getDisplayName();
117 _script->setScriptVar(Descriptor(attribute->getId().replace(".", "_"), attrVarName.replace(".", "_"), attribute->getDocumentation()), QVariant());
118 }
119 }
120
getScript() const121 AttributeScript *Actor::getScript() const {
122 return script;
123 }
124
setScript(AttributeScript * _script)125 void Actor::setScript(AttributeScript *_script) {
126 script->setScriptText(_script->getScriptText());
127 }
128
getCondition() const129 AttributeScript *Actor::getCondition() const {
130 return condition;
131 }
132
getOwner() const133 ActorId Actor::getOwner() const {
134 return owner;
135 }
136
setOwner(const ActorId & owner)137 void Actor::setOwner(const ActorId &owner) {
138 this->owner = owner;
139 }
140
updateActorIds(const QMap<ActorId,ActorId> & actorIdsMap)141 void Actor::updateActorIds(const QMap<ActorId, ActorId> &actorIdsMap) {
142 if (actorIdsMap.contains(owner)) {
143 owner = actorIdsMap[owner];
144 }
145
146 foreach (Attribute *a, this->getAttributes()) {
147 a->updateActorIds(actorIdsMap);
148 }
149 }
150
getProto() const151 ActorPrototype *Actor::getProto() const {
152 return proto;
153 }
154
getId() const155 ActorId Actor::getId() const {
156 return id;
157 }
158
setId(const ActorId & id)159 void Actor::setId(const ActorId &id) {
160 this->id = id;
161 }
162
getLabel() const163 QString Actor::getLabel() const {
164 if (label.isEmpty()) {
165 return QString("%1 %2").arg(getProto()->getDisplayName()).arg(getId());
166 } else {
167 return label;
168 }
169 }
170
setLabel(const QString & l)171 void Actor::setLabel(const QString &l) {
172 label = l;
173 emit si_labelChanged();
174 }
175
getPort(const QString & id) const176 Port *Actor::getPort(const QString &id) const {
177 return ports.value(id);
178 }
179
getPorts() const180 QList<Port *> Actor::getPorts() const {
181 return ports.values();
182 }
183
getInputPorts() const184 QList<Port *> Actor::getInputPorts() const {
185 QList<Port *> l;
186 foreach (Port *p, ports.values()) {
187 if (p->isInput()) {
188 l << p;
189 }
190 }
191 return l;
192 }
193
getOutputPorts() const194 QList<Port *> Actor::getOutputPorts() const {
195 QList<Port *> l;
196 foreach (Port *p, ports.values()) {
197 if (p->isOutput()) {
198 l << p;
199 }
200 }
201 return l;
202 }
203
getEnabledPorts() const204 QList<Port *> Actor::getEnabledPorts() const {
205 QList<Port *> l;
206 foreach (Port *p, ports.values()) {
207 if (p->isEnabled()) {
208 l << p;
209 }
210 }
211 return l;
212 }
213
getEnabledInputPorts() const214 QList<Port *> Actor::getEnabledInputPorts() const {
215 QList<Port *> l;
216 foreach (Port *p, ports.values()) {
217 if (p->isEnabled() && p->isInput()) {
218 l << p;
219 }
220 }
221 return l;
222 }
223
getEnabledOutputPorts() const224 QList<Port *> Actor::getEnabledOutputPorts() const {
225 QList<Port *> l;
226 foreach (Port *p, ports.values()) {
227 if (p->isEnabled() && p->isOutput()) {
228 l << p;
229 }
230 }
231 return l;
232 }
233
setParameter(const QString & name,const QVariant & val)234 void Actor::setParameter(const QString &name, const QVariant &val) {
235 Configuration::setParameter(name, val);
236 updateItemsAvailability(getParameter(name));
237 emit si_modified();
238 }
239
getDescription() const240 ActorDocument *Actor::getDescription() const {
241 return doc;
242 }
243
setDescription(ActorDocument * d)244 void Actor::setDescription(ActorDocument *d) {
245 assert(d != nullptr);
246 doc = d;
247 }
248
getParamAliases() const249 const QMap<QString, QString> &Actor::getParamAliases() const {
250 return paramAliases;
251 }
252
getParamAliases()253 QMap<QString, QString> &Actor::getParamAliases() {
254 return paramAliases;
255 }
256
hasParamAliases() const257 bool Actor::hasParamAliases() const {
258 return !paramAliases.isEmpty();
259 }
260
getAliasHelp() const261 const QMap<QString, QString> &Actor::getAliasHelp() const {
262 return aliasHelpDescs;
263 }
264
getAliasHelp()265 QMap<QString, QString> &Actor::getAliasHelp() {
266 return aliasHelpDescs;
267 }
268
hasAliasHelp() const269 bool Actor::hasAliasHelp() const {
270 foreach (const QString &alias, paramAliases.values()) {
271 if (aliasHelpDescs.contains(alias)) {
272 return true;
273 }
274 }
275 return false;
276 }
277
remap(const QMap<ActorId,ActorId> & m)278 void Actor::remap(const QMap<ActorId, ActorId> &m) {
279 foreach (Port *p, ports) {
280 p->remap(m);
281 }
282 }
283
update(const QMap<ActorId,ActorId> & actorsMapping)284 void Actor::update(const QMap<ActorId, ActorId> &actorsMapping) {
285 foreach (Port *p, getPorts()) {
286 p->updateBindings(actorsMapping);
287 }
288 if (CoreLibConstants::GROUPER_ID == proto->getId()) {
289 updateGrouperSlots(actorsMapping);
290 }
291 }
292
updateGrouperSlots(const QMap<ActorId,ActorId> & actorsMapping)293 void Actor::updateGrouperSlots(const QMap<ActorId, ActorId> &actorsMapping) {
294 SAFE_POINT(1 == getOutputPorts().size(), "Grouper port error 1", );
295 SAFE_POINT(1 == getInputPorts().size(), "Grouper port error 2", );
296 Port *outPort = getOutputPorts().first();
297 SAFE_POINT(outPort->getOutputType()->isMap(), "Grouper port error 3", );
298 QMap<Descriptor, DataTypePtr> outBusMap = outPort->getOutputType()->getDatatypesMap();
299 QMap<Descriptor, DataTypePtr> inBusMap;
300 {
301 Port *inPort = getInputPorts().first();
302 inBusMap = WorkflowUtils::getBusType(inPort);
303 }
304
305 // update in slot attribute
306 {
307 Attribute *attr = getParameter(CoreLibConstants::GROUPER_SLOT_ATTR);
308 QString groupSlot = attr->getAttributeValueWithoutScript<QString>();
309 if (!groupSlot.isEmpty()) {
310 groupSlot = GrouperOutSlot::readable2busMap(groupSlot);
311 U2OpStatus2Log logOs;
312 IntegralBusSlot slot = IntegralBusSlot::fromString(groupSlot, logOs);
313 foreach (const ActorId &oldId, actorsMapping.keys()) {
314 slot.replaceActorId(oldId, actorsMapping[oldId]);
315 }
316 groupSlot = slot.toString();
317 bool found = false;
318 foreach (const Descriptor &d, inBusMap.keys()) {
319 if (d.getId() == groupSlot) {
320 found = true;
321 break;
322 }
323 }
324 if (!found) {
325 groupSlot = "";
326 }
327 attr->setAttributeValue(GrouperOutSlot::busMap2readable(groupSlot));
328 }
329 }
330 // update out slots
331 {
332 GrouperOutSlotAttribute *attr = dynamic_cast<GrouperOutSlotAttribute *>(getParameter(CoreLibConstants::GROUPER_OUT_SLOTS_ATTR));
333 QList<GrouperOutSlot> &outSlots = attr->getOutSlots();
334 QList<GrouperOutSlot>::iterator i = outSlots.begin();
335 while (i != outSlots.end()) {
336 QString in = i->getBusMapInSlotId();
337 U2OpStatus2Log logOs;
338 IntegralBusSlot slot = IntegralBusSlot::fromString(in, logOs);
339 foreach (const ActorId &oldId, actorsMapping.keys()) {
340 slot.replaceActorId(oldId, actorsMapping[oldId]);
341 }
342 in = slot.toString();
343 i->setBusMapInSlotStr(in);
344 bool found = false;
345 foreach (const Descriptor &d, inBusMap.keys()) {
346 if (d.getId() == in) {
347 found = true;
348 break;
349 }
350 }
351 if (!found) {
352 outBusMap.remove(i->getOutSlotId());
353 i = outSlots.erase(i);
354 } else {
355 ++i;
356 }
357 }
358 }
359
360 DataTypePtr newType(new MapDataType(dynamic_cast<Descriptor &>(*(outPort->getType())), outBusMap));
361 outPort->setNewType(newType);
362 }
363
replaceActor(Actor * oldActor,Actor * newActor,const QList<PortMapping> & mappings)364 void Actor::replaceActor(Actor *oldActor, Actor *newActor, const QList<PortMapping> &mappings) {
365 foreach (Port *p, getPorts()) {
366 p->replaceActor(oldActor, newActor, mappings);
367 }
368 if (CoreLibConstants::GROUPER_ID == proto->getId()) {
369 {
370 Attribute *attr = getParameter(CoreLibConstants::GROUPER_SLOT_ATTR);
371 QString groupSlot = attr->getAttributeValueWithoutScript<QString>();
372 groupSlot = GrouperOutSlot::readable2busMap(groupSlot);
373 foreach (const PortMapping &pm, mappings) {
374 IntegralBusUtils::remapPathedSlotString(groupSlot, oldActor->getId(), newActor->getId(), pm);
375 }
376 attr->setAttributeValue(GrouperOutSlot::busMap2readable(groupSlot));
377 }
378
379 {
380 GrouperOutSlotAttribute *attr = dynamic_cast<GrouperOutSlotAttribute *>(getParameter(CoreLibConstants::GROUPER_OUT_SLOTS_ATTR));
381 QList<GrouperOutSlot>::iterator i = attr->getOutSlots().begin();
382 for (; i != attr->getOutSlots().end(); i++) {
383 QString in = i->getBusMapInSlotId();
384 foreach (const PortMapping &pm, mappings) {
385 IntegralBusUtils::remapPathedSlotString(in, oldActor->getId(), newActor->getId(), pm);
386 }
387 i->setBusMapInSlotStr(in);
388 }
389 }
390 }
391 }
392
updateDelegateTags()393 void Actor::updateDelegateTags() {
394 CHECK(editor != nullptr, );
395 foreach (Attribute *influencing, getAttributes()) {
396 QVector<const AttributeRelation *> relations = influencing->getRelations();
397 for (const AttributeRelation *rel : qAsConst(relations)) {
398 PropertyDelegate *dependentDelegate = editor->getDelegate(rel->getRelatedAttrId());
399 if (dependentDelegate == nullptr) {
400 continue;
401 }
402 rel->updateDelegateTags(influencing->getAttributePureValue(), dependentDelegate->tags());
403 }
404 }
405 }
406
updateItemsAvailability()407 void Actor::updateItemsAvailability() {
408 foreach (Attribute *influencing, getAttributes()) {
409 updateItemsAvailability(influencing);
410 }
411 }
412
updateItemsAvailability(const Attribute * influencingAttribute)413 void Actor::updateItemsAvailability(const Attribute *influencingAttribute) {
414 foreach (PortRelationDescriptor *rel, influencingAttribute->getPortRelations()) {
415 Port *dependentPort = getPort(rel->getPortId());
416 CHECK_CONTINUE(dependentPort != nullptr);
417
418 dependentPort->setEnabled(rel->isPortEnabled(influencingAttribute->getAttributePureValue()));
419 }
420
421 foreach (SlotRelationDescriptor *rel, influencingAttribute->getSlotRelations()) {
422 Port *dependentPort = getPort(rel->portId);
423 CHECK_CONTINUE(dependentPort != nullptr);
424
425 const bool isEnabled = rel->isSlotEnabled(influencingAttribute->getAttributePureValue());
426 dependentPort->setVisibleSlot(rel->slotId, isEnabled);
427 }
428 }
429
addCustomValidator(const ValidatorDesc & desc)430 void Actor::addCustomValidator(const ValidatorDesc &desc) {
431 customValidators << desc;
432 }
433
getCustomValidators() const434 const QList<ValidatorDesc> &Actor::getCustomValidators() const {
435 return customValidators;
436 }
437
438 /**
439 * Validates an attribute with URL(s), considering its type.
440 * Attributes with scripts are ignored (the method returns "true").
441 * Attributes with value "Default" (case-insensitive) are ignored.
442 */
validateUrlAttribute(Attribute * attr,UrlAttributeType urlType,NotificationsList & infoList)443 static bool validateUrlAttribute(Attribute *attr, UrlAttributeType urlType, NotificationsList &infoList) {
444 SAFE_POINT(nullptr != attr, "NULL attribute!", false);
445 SAFE_POINT(NotAnUrl != urlType, "Can't pass not an URL to the method!", false);
446
447 if (!attr->getAttributeScript().isEmpty()) {
448 return true;
449 }
450
451 QString urls = attr->getAttributePureValue().toString();
452 if (urls.toLower() == "default") {
453 return true;
454 }
455
456 bool res;
457 switch (urlType) {
458 case DatasetAttr:
459 res = WorkflowUtils::validateDatasets(attr->getAttributePureValue().value<QList<Dataset>>(), infoList);
460 break;
461 case InputFile:
462 res = WorkflowUtils::validateInputFiles(urls, infoList);
463 break;
464 case InputDir:
465 res = WorkflowUtils::validateInputDirs(urls, infoList);
466 break;
467 case OutputFile:
468 res = WorkflowUtils::validateOutputFile(urls, infoList);
469 break;
470 case OutputDir:
471 res = WorkflowUtils::validateOutputDir(urls, infoList);
472 break;
473 default:
474 FAIL("Unexpected value of the URL attribute!", false);
475 }
476 return res;
477 }
478
validateCodePage(const QString & url)479 static bool validateCodePage(const QString &url) {
480 QString url2 = QString::fromLocal8Bit(url.toLocal8Bit());
481 if (url.compare(url2, Qt::CaseSensitive) != 0) {
482 return false;
483 }
484 return true;
485 }
486
487 /**
488 * UGENE-5595
489 * The following validation is very simple:
490 * - convert attr's value from QString to 8Bite byte array
491 * - then, convert back 8Bit to QString
492 * - both QString must be equal
493 */
validateCodePage(Attribute * attr,NotificationsList & infoList)494 static bool validateCodePage(Attribute *attr, NotificationsList &infoList) {
495 SAFE_POINT(nullptr != attr, "NULL attribute!", false);
496
497 QStringList urlsList = WorkflowUtils::getAttributeUrls(attr);
498 if (urlsList.isEmpty()) {
499 return true;
500 }
501
502 // Verify each URL
503 bool res = true;
504 foreach (const QString &url, urlsList) {
505 if (!validateCodePage(url)) {
506 res = false;
507 infoList << WorkflowNotification(L10N::warningCharactersCodePage(attr->getDisplayName()), "", WorkflowNotification::U2_WARNING);
508 }
509 }
510 return res;
511 }
512
validate(NotificationsList & notificationList) const513 bool Actor::validate(NotificationsList ¬ificationList) const {
514 bool result = Configuration::validate(notificationList);
515 foreach (const ValidatorDesc &desc, customValidators) {
516 ActorValidator *v = WorkflowEnv::getActorValidatorRegistry()->findValidator(desc.type);
517 if (nullptr != v) {
518 result &= v->validate(this, notificationList, desc.options);
519 }
520 }
521
522 // Validate URL and Numeric parameters
523 bool urlsRes = true;
524 foreach (Attribute *attr, getParameters()) {
525 SAFE_POINT(nullptr != attr, "NULL attribute!", false);
526 if (!isAttributeVisible(attr)) {
527 continue;
528 }
529 UrlAttributeType urlType = WorkflowUtils::isUrlAttribute(attr, this);
530
531 if (urlType != NotAnUrl) {
532 bool urlAttrValid = validateUrlAttribute(attr, urlType, notificationList);
533 urlsRes = urlsRes && urlAttrValid;
534 }
535
536 if (urlType != NotAnUrl || attr->getFlags().testFlag(Attribute::NeedValidateEncoding)) {
537 // UGENE-5595
538 bool urlAttrValid = validateCodePage(attr, notificationList);
539 Q_UNUSED(urlAttrValid)
540 // we think that this is a warning, so I commented the following line
541 // urlsRes = urlsRes && urlAttrValid;
542 }
543
544 if (attr->getAttributeType() == BaseTypes::NUM_TYPE() && !attr->getAttributePureValue().toString().isEmpty()) {
545 bool ok;
546 attr->getAttributePureValue().toString().toDouble((&ok));
547 result &= ok;
548 if (!ok) {
549 notificationList << WorkflowNotification(L10N::badArgument(attr->getAttributePureValue().toString()));
550 }
551 }
552
553 if (WorkflowUtils::isSharedDbUrlAttribute(attr, this)) {
554 result &= WorkflowUtils::validateSharedDbUrl(attr->getAttributePureValue().toString(), notificationList);
555 }
556 }
557 result = result && urlsRes;
558
559 return result;
560 }
561
562 } // namespace Workflow
563 } // namespace U2
564