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 "IncludedProtoFactoryImpl.h"
23 
24 #include <U2Core/AppContext.h>
25 #include <U2Core/ExternalToolRegistry.h>
26 
27 #include <U2Designer/DelegateEditors.h>
28 
29 #include <U2Lang/Aliasing.h>
30 #include <U2Lang/BaseSlots.h>
31 #include <U2Lang/BaseTypes.h>
32 #include <U2Lang/HRSchemaSerializer.h>
33 #include <U2Lang/LocalDomain.h>
34 #include <U2Lang/WorkflowEnv.h>
35 
36 #include "CmdlineBasedWorkerValidator.h"
37 #include "library/ExternalProcessWorker.h"
38 #include "library/SchemaWorker.h"
39 #include "library/ScriptWorker.h"
40 #include "util/CustomWorkerUtils.h"
41 
42 namespace U2 {
43 using namespace WorkflowSerialize;
44 namespace Workflow {
45 
46 const static QString INPUT_PORT_TYPE("input-for-");
47 const static QString OUTPUT_PORT_TYPE("output-for-");
48 
49 static const QString IN_PORT_ID("in");
50 static const QString OUT_PORT_ID("out");
51 
_getScriptProto(QList<DataTypePtr> input,QList<DataTypePtr> output,QList<Attribute * > attrs,const QString & name,const QString & description,const QString & actorFilePath,bool isAliasName)52 ActorPrototype *IncludedProtoFactoryImpl::_getScriptProto(QList<DataTypePtr> input, QList<DataTypePtr> output, QList<Attribute *> attrs, const QString &name, const QString &description, const QString &actorFilePath, bool isAliasName) {
53     QList<PortDescriptor *> portDescs;
54     QList<Attribute *> attribs = attrs;
55 
56     QMap<Descriptor, DataTypePtr> map;
57     foreach (const DataTypePtr &tptr, input) {
58         if (!tptr || tptr == DataTypePtr()) {
59             coreLog.error(LocalWorkflow::ScriptWorker::tr("For input port was set empty data type"));
60             return nullptr;
61         }
62         map[WorkflowUtils::getSlotDescOfDatatype(tptr)] = tptr;
63     }
64 
65     DataTypePtr inSet(new MapDataType(Descriptor(INPUT_PORT_TYPE + name), map));
66     DataTypeRegistry *dr = WorkflowEnv::getDataTypeRegistry();
67     assert(dr);
68     dr->registerEntry(inSet);
69 
70     map.clear();
71     foreach (const DataTypePtr &tptr, output) {
72         if (!tptr || tptr == DataTypePtr()) {
73             coreLog.error(LocalWorkflow::ScriptWorker::tr("For output port was set empty data type"));
74             return nullptr;
75         }
76         map[WorkflowUtils::getSlotDescOfDatatype(tptr)] = tptr;
77     }
78 
79     DataTypePtr outSet(new MapDataType(Descriptor(OUTPUT_PORT_TYPE + name), map));
80     dr->registerEntry(outSet);
81 
82     Descriptor inDesc(IN_PORT_ID, LocalWorkflow::ScriptWorker::tr("Input data"), LocalWorkflow::ScriptWorker::tr("Input data"));
83     Descriptor outDesc(OUT_PORT_ID, LocalWorkflow::ScriptWorker::tr("Output data"), LocalWorkflow::ScriptWorker::tr("Output data"));
84 
85     if (!input.isEmpty()) {
86         portDescs << new PortDescriptor(inDesc, inSet, /*input*/ true);
87     }
88     if (!output.isEmpty()) {
89         portDescs << new PortDescriptor(outDesc, outSet, /*input*/ false, /*multi*/ true);
90     }
91 
92     QString namePrefix;
93     if (!isAliasName) {
94         namePrefix = LocalWorkflow::ScriptWorkerFactory::ACTOR_ID;
95     }
96     Descriptor desc(namePrefix + name, name, description);
97     ActorPrototype *proto = new IntegralBusActorPrototype(desc, portDescs, attribs);
98     proto->setEditor(new DelegateEditor(QMap<QString, PropertyDelegate *>()));
99     proto->setIconPath(":workflow_designer/images/script.png");
100 
101     proto->setPrompter(new LocalWorkflow::ScriptPromter());
102     proto->setScriptFlag();
103     proto->setNonStandard(actorFilePath);
104 
105     return proto;
106 }
107 
_getExternalToolProto(ExternalProcessConfig * cfg)108 ActorPrototype *IncludedProtoFactoryImpl::_getExternalToolProto(ExternalProcessConfig *cfg) {
109     DataTypeRegistry *dtr = WorkflowEnv::getDataTypeRegistry();
110     QList<PortDescriptor *> portDescs;
111     foreach (const DataConfig &dcfg, cfg->inputs) {
112         QMap<Descriptor, DataTypePtr> map;
113         if (dcfg.type == SEQ_WITH_ANNS) {
114             map[BaseSlots::DNA_SEQUENCE_SLOT()] = BaseTypes::DNA_SEQUENCE_TYPE();
115             map[BaseSlots::ANNOTATION_TABLE_SLOT()] = BaseTypes::ANNOTATION_TABLE_TYPE();
116         } else {
117             map[WorkflowUtils::getSlotDescOfDatatype(dtr->getById(dcfg.type))] = dtr->getById(dcfg.type);
118         }
119 
120         DataTypePtr input(new MapDataType(Descriptor(INPUT_PORT_TYPE + dcfg.attributeId), map));
121         DataTypeRegistry *dr = WorkflowEnv::getDataTypeRegistry();
122         assert(dr);
123         dr->registerEntry(input);
124         portDescs << new PortDescriptor(Descriptor(dcfg.attributeId, dcfg.attrName, dcfg.description), input, true);
125     }
126 
127     QMap<Descriptor, DataTypePtr> map;
128     foreach (const DataConfig &dcfg, cfg->outputs) {
129         if (dcfg.type == SEQ_WITH_ANNS) {
130             map[BaseSlots::ANNOTATION_TABLE_SLOT()] = BaseTypes::ANNOTATION_TABLE_TYPE();
131             map[BaseSlots::DNA_SEQUENCE_SLOT()] = BaseTypes::DNA_SEQUENCE_TYPE();
132         } else {
133             const Descriptor slotDesc = generateUniqueSlotDescriptor(map.keys(), dcfg);
134             map[slotDesc] = dtr->getById(dcfg.type);
135         }
136     }
137     if (!map.isEmpty()) {
138         DataTypePtr outSet(new MapDataType(Descriptor(OUTPUT_PORT_TYPE + cfg->id), map));
139         DataTypeRegistry *dr = WorkflowEnv::getDataTypeRegistry();
140         assert(dr);
141         dr->registerEntry(outSet);
142         Descriptor outDesc(OUT_PORT_ID, LocalWorkflow::ExternalProcessWorker::tr("Output data"), LocalWorkflow::ExternalProcessWorker::tr("Output data"));
143         portDescs << new PortDescriptor(outDesc, outSet, false, true);
144     }
145 
146     Descriptor desc(cfg->id, cfg->name, cfg->description.isEmpty() ? cfg->name : cfg->description);
147 
148     QList<Attribute *> attribs;
149     QMap<QString, PropertyDelegate *> delegates;
150     foreach (const AttributeConfig &acfg, cfg->attrs) {
151         DataTypePtr type;
152         QString descr = acfg.description.isEmpty() ? acfg.type : acfg.description;
153         if (acfg.type == AttributeConfig::INPUT_FILE_URL_TYPE) {
154             type = BaseTypes::STRING_TYPE();
155             delegates[acfg.attributeId] = new URLDelegate("", "", false, false, false, nullptr, "", false, true);
156             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
157         } else if (acfg.type == AttributeConfig::OUTPUT_FILE_URL_TYPE) {
158             type = BaseTypes::STRING_TYPE();
159             delegates[acfg.attributeId] = new URLDelegate("", "", false, false, true, nullptr, "", false, false);
160             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
161         } else if (acfg.type == AttributeConfig::INPUT_FOLDER_URL_TYPE) {
162             type = BaseTypes::STRING_TYPE();
163             delegates[acfg.attributeId] = new URLDelegate("", "", false, true, false, nullptr, "", false, true);
164             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
165         } else if (acfg.type == AttributeConfig::OUTPUT_FOLDER_URL_TYPE) {
166             type = BaseTypes::STRING_TYPE();
167             delegates[acfg.attributeId] = new URLDelegate("", "", false, true, true, nullptr, "", false, false);
168             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
169         } else if (acfg.type == AttributeConfig::STRING_TYPE) {
170             type = BaseTypes::STRING_TYPE();
171             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
172         } else if (acfg.type == AttributeConfig::BOOLEAN_TYPE) {
173             type = BaseTypes::BOOL_TYPE();
174             delegates[acfg.attributeId] = new ComboBoxWithBoolsDelegate();
175             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, (acfg.defaultValue == "true" ? QVariant(true) : QVariant(false)));
176         } else if (acfg.type == AttributeConfig::INTEGER_TYPE) {
177             type = BaseTypes::NUM_TYPE();
178             QVariantMap integerValues;
179             integerValues["minimum"] = QVariant(std::numeric_limits<int>::min());
180             integerValues["maximum"] = QVariant(std::numeric_limits<int>::max());
181             delegates[acfg.attributeId] = new SpinBoxDelegate(integerValues);
182             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
183         } else if (acfg.type == AttributeConfig::DOUBLE_TYPE) {
184             type = BaseTypes::NUM_TYPE();
185             QVariantMap doubleValues;
186             doubleValues["singleStep"] = 0.1;
187             doubleValues["minimum"] = QVariant(std::numeric_limits<double>::lowest());
188             doubleValues["maximum"] = QVariant(std::numeric_limits<double>::max());
189             doubleValues["decimals"] = 6;
190             delegates[acfg.attributeId] = new DoubleSpinBoxDelegate(doubleValues);
191             attribs << new Attribute(Descriptor(acfg.attributeId, acfg.attrName, descr), type, Attribute::None, acfg.defaultValue);
192         }
193     }
194 
195     ActorPrototype *proto = new IntegralBusActorPrototype(desc, portDescs, attribs);
196 
197     proto->setEditor(new DelegateEditor(delegates));
198     proto->setIconPath(":workflow_designer/images/external_cmd_tool.png");
199 
200     proto->setPrompter(new LocalWorkflow::ExternalProcessWorkerPrompter());
201     proto->setNonStandard(cfg->filePath);
202     proto->setValidator(new CmdlineBasedWorkerValidator());
203 
204     QStringList commandIdList = CustomWorkerUtils::getToolIdsFromCommand(cfg->cmdLine);
205     foreach (const QString &id, commandIdList) {
206         proto->addExternalTool(id);
207     }
208 
209     return proto;
210 }
211 
_getSchemaActorProto(Schema * schema,const QString & name,const QString & actorFilePath)212 ActorPrototype *IncludedProtoFactoryImpl::_getSchemaActorProto(Schema *schema, const QString &name, const QString &actorFilePath) {
213     QList<PortDescriptor *> portDescs;
214     QList<Attribute *> attrs;
215 
216     QMap<QString, PropertyDelegate *> delegateMap;
217     QList<Actor *> procs = schema->getProcesses();
218     foreach (Actor *proc, procs) {
219         if (proc->hasParamAliases()) {
220             DelegateEditor *ed = (DelegateEditor *)(proc->getProto()->getEditor());
221             QMap<QString, QString> paramAliases = proc->getParamAliases();
222             foreach (QString attrId, paramAliases.keys()) {
223                 Attribute *origAttr = proc->getParameter(attrId);
224                 Descriptor attrDesc(paramAliases.value(attrId), paramAliases.value(attrId), origAttr->getDocumentation());
225 
226                 attrs << new Attribute(attrDesc, origAttr->getAttributeType(), origAttr->getFlags(), origAttr->getAttributePureValue());
227                 PropertyDelegate *d = ed->getDelegate(attrId);
228                 if (nullptr != d) {
229                     delegateMap[attrDesc.getId()] = d->clone();
230                 }
231             }
232         }
233     }
234 
235     foreach (const PortAlias &portAlias, schema->getPortAliases()) {
236         Descriptor portDescr(portAlias.getAlias(), portAlias.getAlias(), portAlias.getDescription());
237         QMap<Descriptor, DataTypePtr> typeMap;
238 
239         foreach (const SlotAlias &slotAlias, portAlias.getSlotAliases()) {
240             const Port *sourcePort = slotAlias.getSourcePort();
241             QMap<Descriptor, DataTypePtr> sourceTypeMap = sourcePort->getOutputType()->getDatatypesMap();
242             Descriptor slotDescr(slotAlias.getAlias(), slotAlias.getAlias(), "");
243 
244             typeMap[slotDescr] = sourceTypeMap[slotAlias.getSourceSlotId()];
245         }
246         DataTypePtr type(new MapDataType(dynamic_cast<Descriptor &>(*(portAlias.getSourcePort()->getType())), typeMap));
247         PortDescriptor *port = new PortDescriptor(portDescr, type, portAlias.isInput());
248         portDescs << port;
249     }
250 
251     Descriptor desc(name, name, name);
252     ActorPrototype *proto = new IntegralBusActorPrototype(desc, portDescs, attrs);
253     proto->setEditor(new DelegateEditor(delegateMap));
254     proto->setIconPath(":workflow_designer/images/wd.png");
255 
256     proto->setPrompter(new LocalWorkflow::SchemaWorkerPrompter());
257     proto->setSchema(actorFilePath);
258 
259     return proto;
260 }
261 
_registerExternalToolWorker(ExternalProcessConfig * cfg)262 bool IncludedProtoFactoryImpl::_registerExternalToolWorker(ExternalProcessConfig *cfg) {
263     const bool configRegistered = WorkflowEnv::getExternalCfgRegistry()->registerExternalTool(cfg);
264     CHECK(configRegistered, false);
265 
266     DomainFactory *localDomain = WorkflowEnv::getDomainRegistry()->getById(LocalWorkflow::LocalDomainFactory::ID);
267     QScopedPointer<LocalWorkflow::ExternalProcessWorkerFactory> factory(new LocalWorkflow::ExternalProcessWorkerFactory(cfg->id));
268     const bool factoryRegistered = localDomain->registerEntry(factory.data());
269     CHECK_EXT(factoryRegistered, WorkflowEnv::getExternalCfgRegistry()->unregisterConfig(cfg->id), false);
270     factory.take();
271 
272     return true;
273 }
274 
_registerScriptWorker(const QString & actorName)275 void IncludedProtoFactoryImpl::_registerScriptWorker(const QString &actorName) {
276     DomainFactory *localDomain = WorkflowEnv::getDomainRegistry()->getById(LocalWorkflow::LocalDomainFactory::ID);
277     localDomain->registerEntry(new LocalWorkflow::ScriptWorkerFactory(actorName));
278 }
279 
_getExternalToolWorker(const QString & id)280 ExternalProcessConfig *IncludedProtoFactoryImpl::_getExternalToolWorker(const QString &id) {
281     return WorkflowEnv::getExternalCfgRegistry()->getConfigById(id);
282 }
283 
_unregisterExternalToolWorker(const QString & id)284 ExternalProcessConfig *IncludedProtoFactoryImpl::_unregisterExternalToolWorker(const QString &id) {
285     DomainFactory *localDomain = WorkflowEnv::getDomainRegistry()->getById(LocalWorkflow::LocalDomainFactory::ID);
286     delete localDomain->unregisterEntry(id);
287 
288     ExternalProcessConfig *config = WorkflowEnv::getExternalCfgRegistry()->getConfigById(id);
289     WorkflowEnv::getExternalCfgRegistry()->unregisterConfig(id);
290     return config;
291 }
292 
generateUniqueSlotDescriptor(const QList<Descriptor> & existingSlots,const DataConfig & dcfg)293 Descriptor IncludedProtoFactoryImpl::generateUniqueSlotDescriptor(
294     const QList<Descriptor> &existingSlots,
295     const DataConfig &dcfg) {
296     const DataTypeRegistry *dtr = WorkflowEnv::getDataTypeRegistry();
297     Descriptor slotDesc = WorkflowUtils::getSlotDescOfDatatype(
298         dtr->getById(dcfg.type));
299     // add suffix to slot id if there is a slot with the same id
300     const int slotDuplicateCounterStart = 1;
301     int lastSuffixLength = -1;
302     for (int i = slotDuplicateCounterStart; existingSlots.contains(slotDesc); ++i) {
303         if (slotDuplicateCounterStart != i) {
304             const int slotIdBaseLength = slotDesc.getId().length() - lastSuffixLength;
305             slotDesc.setId(slotDesc.getId().left(slotIdBaseLength));
306         }
307         const QString suffix = Constants::DASH + QString::number(i);
308         lastSuffixLength = suffix.length();
309         slotDesc.setId(slotDesc.getId() + suffix);
310     }
311     return slotDesc;
312 }
313 
314 }  // namespace Workflow
315 }  // namespace U2
316