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