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 &notificationList) 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