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 "Schema.h"
23 
24 #include <U2Core/U2OpStatusUtils.h>
25 #include <U2Core/U2SafePoints.h>
26 
27 #include <U2Lang/ActorModel.h>
28 #include <U2Lang/IntegralBusType.h>
29 #include <U2Lang/Wizard.h>
30 #include <U2Lang/WorkflowEnv.h>
31 #include <U2Lang/WorkflowUtils.h>
32 
33 namespace U2 {
34 namespace Workflow {
35 
36 /**************************
37  * Schema
38  **************************/
Schema()39 Schema::Schema()
40     : deepCopy(false) {
41 }
42 
~Schema()43 Schema::~Schema() {
44     reset();
45 }
46 
Schema(const Schema & other)47 Schema::Schema(const Schema &other) {
48     *this = other;
49 }
50 
operator =(const Schema & other)51 Schema &Schema::operator=(const Schema &other) {
52     procs = other.procs;
53     domain = other.domain;
54     graph = ActorBindingsGraph(other.graph);
55     deepCopy = false;
56     portAliases = other.portAliases;
57     includedTypeName = other.includedTypeName;
58     return *this;
59 }
60 
reset()61 void Schema::reset() {
62     if (deepCopy) {
63         qDeleteAll(procs);
64         procs.clear();
65     }
66     graph.clear();
67     qDeleteAll(wizards);
68     wizards.clear();
69 }
70 
applyConfiguration(const QMap<ActorId,QVariantMap> & cfg)71 void Schema::applyConfiguration(const QMap<ActorId, QVariantMap> &cfg) {
72     foreach (Actor *a, procs) {
73         if (cfg.contains(a->getId())) {
74             a->setParameters(cfg[a->getId()]);
75         }
76     }
77 }
78 
actorById(ActorId id) const79 Actor *Schema::actorById(ActorId id) const {
80     return WorkflowUtils::actorById(procs, id);
81 }
82 
actorsByOwnerId(ActorId id) const83 QList<Actor *> Schema::actorsByOwnerId(ActorId id) const {
84     QList<Actor *> res;
85     foreach (Actor *proc, procs) {
86         if (proc->getOwner() == id) {
87             res.append(proc);
88         }
89     }
90     return res;
91 }
92 
getDomain() const93 QString Schema::getDomain() const {
94     return domain;
95 }
96 
setDomain(const QString & d)97 void Schema::setDomain(const QString &d) {
98     domain = d;
99 }
100 
getActorBindingsGraph() const101 const ActorBindingsGraph &Schema::getActorBindingsGraph() const {
102     return graph;
103 }
104 
getProcesses() const105 const QList<Actor *> &Schema::getProcesses() const {
106     return procs;
107 }
108 
addProcess(Actor * a)109 void Schema::addProcess(Actor *a) {
110     assert(a != nullptr);
111     procs.append(a);
112 }
113 
getFlows() const114 QList<Link *> Schema::getFlows() const {
115     return graph.getFlows();
116 }
117 
addFlow(Link * l)118 void Schema::addFlow(Link *l) {
119     assert(l != nullptr);
120     graph.addBinding(l->source(), l->destination());
121 }
122 
setDeepCopyFlag(bool flag)123 void Schema::setDeepCopyFlag(bool flag) {
124     deepCopy = flag;
125 }
126 
hasParamAliases() const127 bool Schema::hasParamAliases() const {
128     foreach (Actor *actor, procs) {
129         if (actor->hasParamAliases()) {
130             return true;
131         }
132     }
133     return false;
134 }
135 
hasAliasHelp() const136 bool Schema::hasAliasHelp() const {
137     foreach (Actor *a, procs) {
138         if (a->hasAliasHelp()) {
139             return true;
140         }
141     }
142     return false;
143 }
144 
hasPortAliases() const145 bool Schema::hasPortAliases() const {
146     return !portAliases.isEmpty();
147 }
148 
getPortAliases() const149 const QList<PortAlias> &Schema::getPortAliases() const {
150     return portAliases;
151 }
152 
addPortAlias(const PortAlias & newAlias)153 bool Schema::addPortAlias(const PortAlias &newAlias) {
154     foreach (const PortAlias &alias, portAliases) {
155         if (alias.getAlias() == newAlias.getAlias()) {
156             return false;
157         }
158         if (alias.getSourcePort() == newAlias.getSourcePort()) {
159             return false;
160         }
161     }
162 
163     portAliases.append(newAlias);
164     return true;
165 }
166 
setPortAliases(const QList<PortAlias> & aliases)167 void Schema::setPortAliases(const QList<PortAlias> &aliases) {
168     portAliases = aliases;
169 }
170 
getTypeName() const171 QString Schema::getTypeName() const {
172     return includedTypeName;
173 }
174 
setTypeName(const QString & typeName)175 void Schema::setTypeName(const QString &typeName) {
176     this->includedTypeName = typeName;
177 }
178 
179 using namespace std;
180 
recursiveExpand(QList<QString> & schemaIds)181 bool Schema::recursiveExpand(QList<QString> &schemaIds) {
182     QMap<Actor *, Schema *> subSchemas;
183 
184     // Expand all processes
185     foreach (Actor *proc, procs) {
186         ActorPrototype *proto = proc->getProto();
187         if (!proto->isSchemaFlagSet()) {
188             continue;
189         }
190 
191         if (schemaIds.contains(proto->getId())) {
192             return false;
193         }
194 
195         Schema *schema = WorkflowEnv::getSchemaActorsRegistry()->getSchema(proto->getId());
196         if (nullptr == schema) {
197             return false;
198         }
199 
200         QList<QString> newIdList(schemaIds);
201         newIdList.append(proto->getId());
202         bool res = schema->recursiveExpand(newIdList);
203         if (!res) {
204             return false;
205         }
206 
207         subSchemas.insert(proc, schema);
208     }
209 
210     // Everything is all right after expanding. So replace expanded processes
211     for (Actor *proc : qAsConst(procs)) {
212         if (!proc->getProto()->isSchemaFlagSet()) {
213             continue;
214         }
215         Schema *schema = subSchemas.value(proc);
216 
217         // set owner
218         foreach (Actor *subProc, schema->getProcesses()) {
219             subProc->setOwner(proc->getId());
220         }
221 
222         // replace parameters
223         foreach (Actor *subProc, schema->getProcesses()) {
224             if (subProc->hasParamAliases()) {
225                 setAliasedAttributes(proc, subProc);
226             }
227         }
228 
229         // replace ports and slots
230         foreach (const PortAlias &subPortAlias, schema->getPortAliases()) {
231             if (subPortAlias.isInput()) {
232                 replaceInLinksAndSlots(proc, subPortAlias);
233             } else {
234                 replaceOutLinks(proc, subPortAlias);
235                 replaceOutSlots(proc, subPortAlias);
236             }
237 
238             if (this->hasPortAliases()) {
239                 replacePortAliases(subPortAlias);
240             }
241         }
242 
243         graph.getBindings().unite(schema->graph.getBindings());
244 
245         // replace procs
246         procs.removeOne(proc);
247         procs.append(schema->getProcesses());
248     }
249 
250     return true;
251 }
252 
expand()253 bool Schema::expand() {
254     QList<QString> emptyList;
255     return this->recursiveExpand(emptyList);
256 }
257 
setAliasedAttributes(Actor * proc,Actor * subProc)258 void Schema::setAliasedAttributes(Actor *proc, Actor *subProc) {
259     QMap<QString, QString> newParamAliases;
260 
261     foreach (QString subAttrId, subProc->getParamAliases().keys()) {
262         QString alias = subProc->getParamAliases().value(subAttrId);
263 
264         QVariant value = proc->getParameter(alias)->getAttributePureValue();
265         subProc->getParameter(subAttrId)->setAttributeValue(value);
266         AttributeScript script = proc->getParameter(alias)->getAttributeScript();
267         subProc->getParameter(subAttrId)->getAttributeScript() = script;
268 
269         if (proc->getParamAliases().keys().contains(alias)) {
270             newParamAliases.insert(subAttrId, proc->getParamAliases().value(alias));
271         }
272     }
273     subProc->getParamAliases() = newParamAliases;
274 }
275 
276 typedef QPair<QString, QString> SlotPair;
277 
replaceInLinksAndSlots(Actor * proc,const PortAlias & portAlias)278 void Schema::replaceInLinksAndSlots(Actor *proc, const PortAlias &portAlias) {
279     Port *port = proc->getPort(portAlias.getAlias());
280     Actor *subProc = portAlias.getSourcePort()->owner();
281     Port *subPort = subProc->getPort(portAlias.getSourcePort()->getId());
282 
283     const QList<Link *> &links = this->getFlows();
284     for (Link *link: qAsConst(links)) {
285         if (link->destination() == port) {
286             // replace ports link
287             removeFlow(link);
288             link->connect(link->source(), subPort);
289             addFlow(link);
290 
291             // replace slots links and paths
292             Attribute *busMapAttr = port->getParameter(IntegralBusPort::BUS_MAP_ATTR_ID);
293             Attribute *pathsAttr = port->getParameter(IntegralBusPort::PATHS_ATTR_ID);
294             StrStrMap busMap = busMapAttr->getAttributeValueWithoutScript<StrStrMap>();
295             SlotPathMap pathMap = pathsAttr->getAttributeValueWithoutScript<SlotPathMap>();
296             StrStrMap subBusMap;
297             SlotPathMap subPathMap;
298 
299             QList<SlotAlias> portSlotAliases = portAlias.getSlotAliases();
300             for (const SlotAlias &slotAlias : qAsConst(portSlotAliases)) {
301                 subBusMap[slotAlias.getSourceSlotId()] = busMap[slotAlias.getAlias()];
302 
303                 QList<SlotPair> pathMapKeys = pathMap.keys();
304                 for (const SlotPair &slotPair : qAsConst(pathMapKeys)) {
305                     if (slotAlias.getAlias() == slotPair.first) {
306                         SlotPair subPair(slotAlias.getSourceSlotId(), slotPair.second);
307                         foreach (const QStringList &p, pathMap.values(slotPair)) {
308                             subPathMap.insertMulti(subPair, p);
309                         }
310                     }
311                 }
312             }
313             subPort->getParameter(IntegralBusPort::BUS_MAP_ATTR_ID)->setAttributeValue(qVariantFromValue(subBusMap));
314             subPort->getParameter(IntegralBusPort::PATHS_ATTR_ID)->setAttributeValue(qVariantFromValue(subPathMap));
315         }
316     }
317 }
318 
replaceOutLinks(Actor * origProc,const PortAlias & portAlias)319 void Schema::replaceOutLinks(Actor *origProc, const PortAlias &portAlias) {
320     Port *port = origProc->getPort(portAlias.getAlias());
321     Actor *subProc = portAlias.getSourcePort()->owner();
322     Port *subPort = subProc->getPort(portAlias.getSourcePort()->getId());
323 
324     foreach (Link *link, this->getFlows()) {
325         if (link->source() == port) {
326             // replace only ports link
327             removeFlow(link);
328             link->connect(subPort, link->destination());
329             addFlow(link);
330         }
331     }
332 }
333 
replaceOutSlots(Actor * origProc,const PortAlias & portAlias)334 void Schema::replaceOutSlots(Actor *origProc, const PortAlias &portAlias) {
335     // replace slots links
336     foreach (Actor *proc, procs) {
337         foreach (Port *p, proc->getInputPorts()) {
338             Attribute *a = p->getParameter(IntegralBusPort::BUS_MAP_ATTR_ID);
339             StrStrMap busMap = a->getAttributeValueWithoutScript<StrStrMap>();
340             StrStrMap newMap;
341 
342             QMapIterator<QString, QString> it(busMap);
343             while (it.hasNext()) {
344                 it.next();
345                 // replace ids at slots' values
346                 QString value = it.value();
347                 foreach (const SlotAlias &slotAlias, portAlias.getSlotAliases()) {
348                     QString origSlotId = slotAlias.getAlias();
349                     QString subSlotId = slotAlias.getSourceSlotId();
350 
351                     QString slotString = origProc->getId() + ":" + origSlotId;
352                     int idPos = value.indexOf(slotString);
353                     while (idPos >= 0) {
354                         Actor *subProc = slotAlias.getSourcePort()->owner();
355                         value.remove(idPos, slotString.length());
356                         value.insert(idPos, subProc->getId() + ":" + subSlotId);
357                         idPos = value.indexOf(slotString);
358                     }
359                 }
360                 newMap.insert(it.key(), value);
361             }
362 
363             a->setAttributeValue(qVariantFromValue(newMap));
364         }
365     }
366 }
367 
replacePortAliases(const PortAlias & subPortAlias)368 void Schema::replacePortAliases(const PortAlias &subPortAlias) {
369     // replace port aliases
370     QList<PortAlias> newPortAliases;
371     foreach (PortAlias origPortAlias, this->portAliases) {
372         if (origPortAlias.getSourcePort()->getId() == subPortAlias.getAlias()) {
373             origPortAlias.setNewSourcePort(subPortAlias.getSourcePort());
374         }
375 
376         // replace slot aliases
377         QList<SlotAlias> newSlotAliases;
378         foreach (const SlotAlias &origSlotAlias, origPortAlias.getSlotAliases()) {
379             if (origSlotAlias.getSourcePort()->getId() == subPortAlias.getAlias()) {
380                 foreach (const SlotAlias &subSlotAlias, subPortAlias.getSlotAliases()) {
381                     if (subSlotAlias.getAlias() == origSlotAlias.getSourceSlotId()) {
382                         SlotAlias newSlotAlias(subSlotAlias.getSourcePort(), subSlotAlias.getSourceSlotId(), origSlotAlias.getAlias());
383                         newSlotAliases.append(newSlotAlias);
384                         break;
385                     }
386                 }
387             } else {
388                 newSlotAliases.append(origSlotAlias);
389             }
390         }
391         origPortAlias.setNewSlotAliases(newSlotAliases);
392         newPortAliases.append(origPortAlias);
393     }
394     this->portAliases = newPortAliases;
395 }
396 
getWizards() const397 const QList<Wizard *> &Schema::getWizards() const {
398     return wizards;
399 }
400 
setWizards(const QList<Wizard * > & value)401 void Schema::setWizards(const QList<Wizard *> &value) {
402     qDeleteAll(wizards);
403     wizards = value;
404 }
405 
takeWizards()406 QList<Wizard *> Schema::takeWizards() {
407     QList<Wizard *> result = wizards;
408     wizards.clear();
409     return result;
410 }
411 
removeProcess(Actor * actor)412 void Schema::removeProcess(Actor *actor) {
413     // remove actors flows
414     foreach (Port *p, actor->getPorts()) {
415         foreach (Link *l, p->getLinks()) {
416             removeFlow(l);
417         }
418     }
419 
420     // remove actor from port aliases
421     QList<Port *> ports = actor->getPorts();
422     QList<PortAlias>::iterator i = portAliases.begin();
423     while (i != portAliases.end()) {
424         Port *p = const_cast<Port *>(i->getSourcePort());
425         if (ports.contains(p)) {
426             i = portAliases.erase(i);
427         } else {
428             ++i;
429         }
430     }
431 
432     procs.removeOne(actor);
433     update();
434 }
435 
update()436 void Schema::update() {
437     update(QMap<ActorId, ActorId>());
438 }
439 
update(const QMap<ActorId,ActorId> & actorsMapping)440 void Schema::update(const QMap<ActorId, ActorId> &actorsMapping) {
441     // update actors from the first level of the graph to the last one
442     QMap<int, QList<Actor *>> top = graph.getTopologicalSortedGraph(procs);
443     int beginLevel = top.size() - 1;
444     for (int level = beginLevel; level >= 0; level--) {
445         foreach (Actor *a, top[level]) {
446             a->update(actorsMapping);
447         }
448     }
449 }
450 
removeFlow(Link * l)451 void Schema::removeFlow(Link *l) {
452     if (graph.contains(l->source(), l->destination())) {
453         graph.removeBinding(l->source(), l->destination());
454         l->disconnect();
455         // TODO: delete l;
456     }
457 }
458 
uniqueActorId(const QString & id,const QList<Actor * > & procs)459 ActorId Schema::uniqueActorId(const QString &id, const QList<Actor *> &procs) {
460     QStringList uniqueIds;
461     foreach (Actor *a, procs) {
462         uniqueIds << aid2str(a->getId());
463     }
464     QString result = WorkflowUtils::createUniqueString(id, "-", uniqueIds);
465     return str2aid(result);
466 }
467 
renameProcess(const ActorId & oldId,const ActorId & newId)468 void Schema::renameProcess(const ActorId &oldId, const ActorId &newId) {
469     Actor *actor = actorById(oldId);
470     CHECK(nullptr != actor, );
471 
472     actor->setId(newId);
473     QMap<ActorId, ActorId> m;
474     m[oldId] = newId;
475     foreach (Port *p, actor->getPorts()) {
476         p->remap(m);
477     }
478     update(m);
479 }
480 
481 namespace {
removeAliasesDupliucates(const QList<Actor * > & actors,Actor * newActor)482 QStringList removeAliasesDupliucates(const QList<Actor *> &actors, Actor *newActor) {
483     QStringList removed;
484     QStringList allAliases;
485     foreach (Actor *actor, actors) {
486         allAliases << actor->getParamAliases().values();
487     }
488     QMap<QString, QString> newAliases = newActor->getParamAliases();
489     foreach (const QString &key, newAliases.keys()) {
490         QString alias = newAliases.value(key);
491         if (allAliases.contains(alias)) {
492             newActor->getParamAliases().remove(key);
493             newActor->getAliasHelp().remove(alias);
494             removed << alias;
495         }
496     }
497     return removed;
498 }
499 }  // namespace
500 
merge(Schema & other)501 void Schema::merge(Schema &other) {
502     foreach (Actor *newActor, other.procs) {
503         QStringList removed = removeAliasesDupliucates(procs, newActor);
504         foreach (const QString &alias, removed) {
505             coreLog.error(QObject::tr("Duplicate alias '%1'. It has been removed").arg(alias));
506         }
507         procs << newActor;
508     }
509     graph.getBindings().unite(other.graph.getBindings());
510     portAliases << other.portAliases;
511 }
512 
replaceProcess(Actor * oldActor,Actor * newActor,const QList<PortMapping> & mappings)513 void Schema::replaceProcess(Actor *oldActor, Actor *newActor, const QList<PortMapping> &mappings) {
514     CHECK(procs.contains(oldActor), );
515     CHECK(!procs.contains(newActor), );
516     QMap<int, QList<Actor *>> top = graph.getTopologicalSortedGraph(procs);
517 
518     // replace actors flows
519     foreach (Port *p, oldActor->getPorts()) {
520         U2OpStatus2Log os;
521         PortMapping pm = PortMapping::getMappingBySrcPort(p->getId(), mappings, os);
522         if (os.hasError()) {
523             continue;
524         }
525         foreach (Link *l, p->getLinks()) {
526             Port *p1 = l->source() == p ? l->destination() : l->source();
527             Port *p2 = newActor->getPort(pm.getDstId());
528             removeFlow(l);
529             Link *newLink = new Link(p1, p2);
530             addFlow(newLink);
531             if (p2->isInput()) {
532                 IntegralBusPort *oldPort = dynamic_cast<IntegralBusPort *>(p);
533                 IntegralBusPort *newPort = dynamic_cast<IntegralBusPort *>(p2);
534                 newPort->copyInput(oldPort, pm);
535             }
536         }
537     }
538 
539     int beginLevel = top.size() - 1;
540     for (int level = beginLevel; level >= 0; level--) {
541         foreach (Actor *a, top[level]) {
542             if (a != oldActor) {
543                 a->replaceActor(oldActor, newActor, mappings);
544             }
545         }
546     }
547 
548     procs.removeOne(oldActor);
549     procs.append(newActor);
550 }
551 
552 /************************************************************************/
553 /* ActorVisualData */
554 /************************************************************************/
ActorVisualData()555 ActorVisualData::ActorVisualData() {
556     initialize();
557 }
558 
ActorVisualData(const ActorId & _actorId)559 ActorVisualData::ActorVisualData(const ActorId &_actorId)
560     : actorId(_actorId) {
561     initialize();
562 }
563 
initialize()564 void ActorVisualData::initialize() {
565     posInited = false;
566     styleInited = false;
567     colorInited = false;
568     fontInited = false;
569     rectInited = false;
570 }
571 
getActorId() const572 ActorId ActorVisualData::getActorId() const {
573     return actorId;
574 }
575 
setActorId(const ActorId & value)576 void ActorVisualData::setActorId(const ActorId &value) {
577     actorId = value;
578 }
579 
getPos(bool & contains) const580 QPointF ActorVisualData::getPos(bool &contains) const {
581     contains = posInited;
582     return pos;
583 }
584 
getStyle(bool & contains) const585 QString ActorVisualData::getStyle(bool &contains) const {
586     contains = styleInited;
587     return styleId;
588 }
589 
getColor(bool & contains) const590 QColor ActorVisualData::getColor(bool &contains) const {
591     contains = colorInited;
592     return color;
593 }
594 
getFont(bool & contains) const595 QFont ActorVisualData::getFont(bool &contains) const {
596     contains = fontInited;
597     return font;
598 }
599 
getRect(bool & contains) const600 QRectF ActorVisualData::getRect(bool &contains) const {
601     contains = rectInited;
602     return rect;
603 }
604 
getPortAngle(const QString & portId,bool & contains) const605 qreal ActorVisualData::getPortAngle(const QString &portId, bool &contains) const {
606     contains = angleMap.contains(portId);
607     return angleMap.value(portId, 0.0);
608 }
609 
setPos(const QPointF & value)610 void ActorVisualData::setPos(const QPointF &value) {
611     posInited = true;
612     pos = value;
613 }
614 
setStyle(const QString & value)615 void ActorVisualData::setStyle(const QString &value) {
616     styleInited = true;
617     styleId = value;
618 }
619 
setColor(const QColor & value)620 void ActorVisualData::setColor(const QColor &value) {
621     colorInited = true;
622     color = value;
623 }
624 
setFont(const QFont & value)625 void ActorVisualData::setFont(const QFont &value) {
626     fontInited = true;
627     font = value;
628 }
629 
setRect(const QRectF & value)630 void ActorVisualData::setRect(const QRectF &value) {
631     rectInited = true;
632     rect = value;
633 }
634 
setPortAngle(const QString & portId,qreal value)635 void ActorVisualData::setPortAngle(const QString &portId, qreal value) {
636     angleMap[portId] = value;
637 }
638 
getAngleMap() const639 QMap<QString, qreal> ActorVisualData::getAngleMap() const {
640     return angleMap;
641 }
642 
643 /**************************
644  * Metadata
645  **************************/
Metadata()646 Metadata::Metadata() {
647     reset();
648 }
649 
reset()650 void Metadata::reset() {
651     name = QString();
652     comment = QString();
653     url = QString();
654     scalePercent = 100;
655     isSampleWorkflow = false;
656     estimationsCode.clear();
657 
658     resetVisual();
659 }
660 
resetVisual()661 void Metadata::resetVisual() {
662     actorVisual.clear();
663     textPosMap.clear();
664 }
665 
getActorVisualData(const ActorId & actorId,bool & contains) const666 ActorVisualData Metadata::getActorVisualData(const ActorId &actorId, bool &contains) const {
667     contains = actorVisual.contains(actorId);
668     return actorVisual.value(actorId, ActorVisualData());
669 }
670 
setActorVisualData(const ActorVisualData & data)671 void Metadata::setActorVisualData(const ActorVisualData &data) {
672     actorVisual[data.getActorId()] = data;
673 }
674 
getTextPos(const ActorId & srcActorId,const QString & srcPortId,const ActorId & dstActorId,const QString & dstPortId,bool & contains) const675 QPointF Metadata::getTextPos(const ActorId &srcActorId, const QString &srcPortId, const ActorId &dstActorId, const QString &dstPortId, bool &contains) const {
676     QString linkStr = getLinkString(srcActorId, srcPortId, dstActorId, dstPortId);
677     contains = textPosMap.contains(linkStr);
678     return textPosMap.value(linkStr, QPointF());
679 }
680 
setTextPos(const ActorId & srcActorId,const QString & srcPortId,const ActorId & dstActorId,const QString & dstPortId,const QPointF & value)681 void Metadata::setTextPos(const ActorId &srcActorId, const QString &srcPortId, const ActorId &dstActorId, const QString &dstPortId, const QPointF &value) {
682     QString linkStr = getLinkString(srcActorId, srcPortId, dstActorId, dstPortId);
683     textPosMap[linkStr] = value;
684 }
685 
removeActorMeta(const ActorId & actorId)686 void Metadata::removeActorMeta(const ActorId &actorId) {
687     actorVisual.remove(actorId);
688 
689     foreach (const QString &linkStr, textPosMap.keys()) {
690         if (isActorLinked(actorId, linkStr)) {
691             textPosMap.remove(linkStr);
692         }
693     }
694 }
695 
getPortString(const ActorId & actorId,const QString & portId) const696 QString Metadata::getPortString(const ActorId &actorId, const QString &portId) const {
697     return actorId + "." + portId;
698 }
699 
getActorId(const QString & portStr) const700 ActorId Metadata::getActorId(const QString &portStr) const {
701     QStringList tokens = portStr.split(".");
702     CHECK(2 == tokens.size(), ActorId(""));
703     return tokens[0];
704 }
705 
getLinkString(const ActorId & srcActorId,const QString & srcPortId,const ActorId & dstActorId,const QString & dstPortId) const706 QString Metadata::getLinkString(const ActorId &srcActorId, const QString &srcPortId, const ActorId &dstActorId, const QString &dstPortId) const {
707     return getPortString(srcActorId, srcPortId) + "->" +
708            getPortString(dstActorId, dstPortId);
709 }
710 
isActorLinked(const ActorId & actorId,const QString & linkStr) const711 bool Metadata::isActorLinked(const ActorId &actorId, const QString &linkStr) const {
712     QStringList tokens = linkStr.split("->");
713     CHECK(2 == tokens.size(), false);
714 
715     QStringList srcTokens = tokens[0].split(".");
716     CHECK(2 == srcTokens.size(), false);
717     QStringList dstTokens = tokens[1].split(".");
718     CHECK(2 == dstTokens.size(), false);
719 
720     return (srcTokens[0] == actorId) || (dstTokens[0] == actorId);
721 }
722 
getActorsVisual() const723 QList<ActorVisualData> Metadata::getActorsVisual() const {
724     return actorVisual.values();
725 }
726 
getTextPosMap() const727 QMap<QString, QPointF> Metadata::getTextPosMap() const {
728     return textPosMap;
729 }
730 
setSampleMark(bool isSample)731 void Metadata::setSampleMark(bool isSample) {
732     isSampleWorkflow = isSample;
733 }
734 
isSample() const735 bool Metadata::isSample() const {
736     return isSampleWorkflow;
737 }
738 
renameActors(const QMap<ActorId,ActorId> & actorsMapping)739 void Metadata::renameActors(const QMap<ActorId, ActorId> &actorsMapping) {
740     foreach (const ActorId &oldId, actorsMapping.keys()) {
741         if (actorVisual.contains(oldId)) {
742             ActorId newId = actorsMapping[oldId];
743             ActorVisualData visual = actorVisual.take(oldId);
744             visual.setActorId(newId);
745             actorVisual[newId] = visual;
746         }
747     }
748 
749     foreach (const QString &oldLinkStr, textPosMap.keys()) {
750         QString newLinkStr = renameLink(oldLinkStr, actorsMapping);
751         if (newLinkStr != oldLinkStr) {
752             textPosMap[newLinkStr] = textPosMap[oldLinkStr];
753             textPosMap.remove(oldLinkStr);
754         }
755     }
756 }
757 
renameLink(const QString & linkStr,const QMap<ActorId,ActorId> & actorsMapping) const758 QString Metadata::renameLink(const QString &linkStr, const QMap<ActorId, ActorId> &actorsMapping) const {
759     QStringList tokens = linkStr.split("->");
760     CHECK(2 == tokens.size(), linkStr);
761 
762     QStringList srcTokens = tokens[0].split(".");
763     CHECK(2 == srcTokens.size(), linkStr);
764     QStringList dstTokens = tokens[1].split(".");
765     CHECK(2 == dstTokens.size(), linkStr);
766 
767     foreach (const ActorId &oldId, actorsMapping.keys()) {
768         if (srcTokens[0] == oldId) {
769             srcTokens[0] = actorsMapping[oldId];
770         }
771         if (dstTokens[0] == oldId) {
772             dstTokens[0] = actorsMapping[oldId];
773         }
774     }
775     return getLinkString(srcTokens[0], srcTokens[1], dstTokens[0], dstTokens[1]);
776 }
777 
renameLink(const QString & linkStr,const ActorId & oldId,const ActorId & newId,const QList<PortMapping> & mappings) const778 QString Metadata::renameLink(const QString &linkStr, const ActorId &oldId, const ActorId &newId, const QList<PortMapping> &mappings) const {
779     QStringList tokens = linkStr.split("->");
780     CHECK(2 == tokens.size(), linkStr);
781 
782     QStringList srcTokens = tokens[0].split(".");
783     CHECK(2 == srcTokens.size(), linkStr);
784     QStringList dstTokens = tokens[1].split(".");
785     CHECK(2 == dstTokens.size(), linkStr);
786 
787     if (srcTokens[0] == oldId) {
788         U2OpStatus2Log os;
789         PortMapping m = PortMapping::getMappingBySrcPort(srcTokens[1], mappings, os);
790         srcTokens[0] = newId;
791         srcTokens[1] = m.getDstId();
792     }
793     if (dstTokens[0] == oldId) {
794         U2OpStatus2Log os;
795         PortMapping m = PortMapping::getMappingBySrcPort(dstTokens[1], mappings, os);
796         dstTokens[0] = newId;
797         dstTokens[1] = m.getDstId();
798     }
799     return getLinkString(srcTokens[0], srcTokens[1], dstTokens[0], dstTokens[1]);
800 }
801 
mergeVisual(const Metadata & other)802 void Metadata::mergeVisual(const Metadata &other) {
803     actorVisual.unite(other.actorVisual);
804     textPosMap.unite(other.textPosMap);
805 }
806 
replaceProcess(const ActorId & oldId,const ActorId & newId,const QList<PortMapping> & mappings)807 void Metadata::replaceProcess(const ActorId &oldId, const ActorId &newId, const QList<PortMapping> &mappings) {
808     bool contains = false;
809     if (actorVisual.contains(oldId)) {
810         ActorVisualData oldV = actorVisual[oldId];
811         ActorVisualData newV(newId);
812         QPointF p = oldV.getPos(contains);
813         if (contains) {
814             newV.setPos(p);
815         }
816         QString s = oldV.getStyle(contains);
817         if (contains) {
818             newV.setStyle(s);
819         }
820         QColor c = oldV.getColor(contains);
821         if (contains) {
822             newV.setColor(c);
823         }
824         QFont f = oldV.getFont(contains);
825         if (contains) {
826             newV.setFont(f);
827         }
828         QRectF r = oldV.getRect(contains);
829         if (contains) {
830             newV.setRect(r);
831         }
832         actorVisual.remove(oldId);
833         actorVisual[newId] = newV;
834     }
835     foreach (const QString &linkStr, textPosMap.keys()) {
836         QString newLinkStr = renameLink(linkStr, oldId, newId, mappings);
837         if (newLinkStr != linkStr) {
838             textPosMap[newLinkStr] = textPosMap[linkStr];
839             textPosMap.remove(linkStr);
840         }
841     }
842 }
843 
844 /**************************
845  * ActorBindingGraph
846  **************************/
validateGraph(QString &) const847 bool ActorBindingsGraph::validateGraph(QString &) const {
848     return true;
849 }
850 
addBinding(Port * source,Port * dest)851 bool ActorBindingsGraph::addBinding(Port *source, Port *dest) {
852     QList<Port *> ports;
853     if (bindings.contains(source)) {
854         ports = bindings.value(source);
855         if (ports.contains(dest)) {
856             return false;
857         }
858     }
859     ports.append(dest);
860     bindings.insert(source, ports);
861     return true;
862 }
863 
contains(Port * source,Port * dest) const864 bool ActorBindingsGraph::contains(Port *source, Port *dest) const {
865     if (bindings.contains(source)) {
866         const QList<Port *> &ports = bindings[source];
867         return ports.contains(dest);
868     }
869     return false;
870 }
871 
removeBinding(Port * source,Port * dest)872 void ActorBindingsGraph::removeBinding(Port *source, Port *dest) {
873     if (bindings.contains(source)) {
874         QList<Port *> &ports = bindings[source];
875         ports.removeOne(dest);
876         if (ports.isEmpty()) {
877             bindings.remove(source);
878         }
879     }
880 }
881 
getBindings() const882 const QMap<Port *, QList<Port *>> &ActorBindingsGraph::getBindings() const {
883     return bindings;
884 }
885 
getBindings()886 QMap<Port *, QList<Port *>> &ActorBindingsGraph::getBindings() {
887     return bindings;
888 }
889 
getTopologicalSortedGraph(QList<Actor * > actors) const890 QMap<int, QList<Actor *>> ActorBindingsGraph::getTopologicalSortedGraph(QList<Actor *> actors) const {
891     QMap<Actor *, QList<Port *>> graph;
892     foreach (Port *source, bindings.keys()) {
893         if (graph.contains(source->owner())) {
894             graph[source->owner()].append(bindings.value(source));
895         } else {
896             graph.insert(source->owner(), bindings.value(source));
897         }
898     }
899     QMap<int, QList<Actor *>> result;
900 
901     int vertexLabel = 0;
902     while (!graph.isEmpty()) {
903         QList<Actor *> endVertexes;
904         {
905             foreach (Actor *a, actors) {
906                 if (!graph.keys().contains(a)) {  // so, there is no arcs from this actor
907                     endVertexes.append(a);
908                 }
909             }
910         }
911         result.insert(vertexLabel, endVertexes);
912 
913         foreach (Actor *a, graph.keys()) {
914             QList<Port *> ports = graph.value(a);
915             foreach (Port *p, ports) {
916                 if (endVertexes.contains(p->owner())) {
917                     ports.removeOne(p);
918                 }
919             }
920             if (ports.isEmpty()) {
921                 graph.remove(a);
922             } else {
923                 graph.insert(a, ports);
924             }
925         }
926 
927         foreach (Actor *a, endVertexes) {
928             actors.removeOne(a);
929         }
930         vertexLabel++;
931     }
932     result.insert(vertexLabel, actors);
933 
934     return result;
935 }
936 
clear()937 void ActorBindingsGraph::clear() {
938     bindings.clear();
939 }
940 
isEmpty() const941 bool ActorBindingsGraph::isEmpty() const {
942     return bindings.isEmpty();
943 }
944 
getFlows() const945 QList<Link *> ActorBindingsGraph::getFlows() const {
946     QList<Link *> result;
947     foreach (Port *src, bindings.keys()) {
948         foreach (Link *l, src->getLinks()) {
949             SAFE_POINT(l->source() == src, "Link's source port mismatch", result);
950             Port *dst = l->destination();
951             SAFE_POINT(bindings[src].contains(dst), "Link's destination port mismatch", result);
952             result << l;
953         }
954     }
955     return result;
956 }
957 
958 }  // namespace Workflow
959 
960 }  // namespace U2
961