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 #ifndef _U2_WORKFLOW_ATTR_H_
23 #define _U2_WORKFLOW_ATTR_H_
24 
25 #include <cassert>
26 
27 #include <QScriptValue>
28 #include <QVariant>
29 
30 #include <U2Core/ScriptEngine.h>
31 #include <U2Core/ScriptTask.h>
32 #include <U2Core/Task.h>
33 
34 #include <U2Lang/AttributeRelation.h>
35 #include <U2Lang/Datatype.h>
36 #include <U2Lang/Descriptor.h>
37 #include <U2Lang/ScriptLibrary.h>
38 #include <U2Lang/SupportClass.h>
39 #include <U2Lang/WorkflowContext.h>
40 #include <U2Lang/WorkflowScriptEngine.h>
41 
42 #include "PortRelation.h"
43 #include "SlotRelation.h"
44 
45 namespace U2 {
46 
47 typedef QString ActorId;
str2aid(const QString & s)48 inline ActorId str2aid(const QString &s) {
49     return s;
50 }
aid2str(const ActorId & s)51 inline QString aid2str(const ActorId &s) {
52     return s;
53 }
54 
55 /**
56  * attribute value can be obtained from script
57  */
58 class U2LANG_EXPORT AttributeScript {
59 public:
60     AttributeScript(const QString &text);
AttributeScript()61     AttributeScript() {
62     }
63 
64     bool isEmpty() const;
65 
66     void setScriptText(const QString &t);
67     const QString &getScriptText() const;
68 
69     void setScriptVar(const Descriptor &desc, const QVariant &val);
70     const QMap<Descriptor, QVariant> &getScriptVars() const;
71     void clearScriptVars();
72 
73     bool hasVarWithId(const QString &varName) const;
74     bool hasVarWithDesc(const QString &varName) const;
75     void setVarValueWithId(const QString &varName, const QVariant &value);
76 
77 private:
78     QString text;
79     QMap<Descriptor, QVariant> vars;  // (desc, val)
80 
81 };  // AttributeScript
82 
83 /**
84  * Existing types of attributes
85  */
86 enum AttributeGroup {
87     COMMON_GROUP,
88     MARKER_GROUP,
89     GROUPER_SLOT_GROUP
90 };
91 
92 /**
93  * Value of certain type that can be identified by (descriptors) id
94  */
95 class U2LANG_EXPORT Attribute : public Descriptor {
96 public:
97     enum Flag {
98         None = 0,
99         CanBeEmpty = 1,  // it has meaning only for required attributes, allows the required attribute to be empty
100         NeedValidateEncoding = 2,  // it has meaning that the attribute is some string which must be validated against Unicode -> Byte convertion
101         // for example many plugins/external tools accept filenames as char*, but QString -> char* convertion may
102         // produce invalid values if QString's encoding is not the same as system Locale Code Page
103         Required = 4,  // values of required attributes cannot be empty, if the appropriate flag is not set
104         Hidden = 8  // Hidden attribute
105     };
106     Q_DECLARE_FLAGS(Flags, Flag)
107 
108     Attribute(const Descriptor &descriptor, const DataTypePtr type, const Flags flags = None, const QVariant &defaultValue = QVariant());
109     Attribute(const Descriptor &descriptor, const DataTypePtr type, bool required, const QVariant &defaultValue = QVariant());
110     ~Attribute();
111 
112     // getters/setters
113     const DataTypePtr getAttributeType() const;
114     bool isRequiredAttribute() const;
115     bool canBeEmpty() const;
116     bool needValidateEncoding() const;
117     Flags getFlags() const;
118 
119     virtual void setAttributeValue(const QVariant &newVal);
120     // attribute value is kept in qvariant
121     // but it can be transformed to value of specific type using scripting or not (see getAttributeValue)
122     virtual const QVariant &getAttributePureValue() const;
123     virtual const QVariant &getDefaultPureValue() const;
124     virtual bool isDefaultValue() const;
125 
126     // base realization without scripting. to support scripting for other types: see template realizations
127     template<typename T>
getAttributeValue(Workflow::WorkflowContext *)128     T getAttributeValue(Workflow::WorkflowContext *) const {
129         return getAttributeValueWithoutScript<T>();
130     }
131 
132     template<typename T>
getAttributeValueWithoutScript()133     T getAttributeValueWithoutScript() const {
134         return value.value<T>();
135     }
136 
137     const AttributeScript &getAttributeScript() const;
138     // used to change script data
139     AttributeScript &getAttributeScript();
140 
141     // stores value and script data in variant
142     // used in saving schema to xml
143     QVariant toVariant() const;
144     // reads value and script from variant
145     // used in reading schema from xml
146     bool fromVariant(const QVariant &variant);
147     bool isEmptyString() const;
148     void addRelation(const AttributeRelation *relation);
149     QVector<const AttributeRelation *> &getRelations();
150 
151     void addPortRelation(PortRelationDescriptor *relationDesc);
152     const QList<PortRelationDescriptor *> &getPortRelations() const;
153 
154     void addSlotRelation(SlotRelationDescriptor *relationDesc);
155     const QList<SlotRelationDescriptor *> &getSlotRelations() const;
156 
157     virtual bool isEmpty() const;
158     virtual Attribute *clone();
159     virtual AttributeGroup getGroup();
160 
161     /**
162      * Ids of actors in the scheme can be changed (after copy-paste, for example).
163      * It is needed to update all of ids which are used by this attribute
164      */
165     virtual void updateActorIds(const QMap<ActorId, ActorId> &actorIdsMap);
166 
167     virtual bool validate(NotificationsList &notificationList);
168 
169 private:
170     void debugCheckAttributeId() const;
171     void copy(const Attribute &other);
172 
173 protected:
174     Attribute(const Attribute &other);
175     Attribute &operator=(const Attribute &other);
176 
177     // type of value
178     DataTypePtr type;
179     // Different additional options
180     Flags flags;
181     // pure value and default pure value. if script exists, value should be processed throw it
182     QVariant value;
183     QVariant defaultValue;
184     // script text and variable values for script evaluating
185     // script variables get values only in runtime
186     AttributeScript scriptData;
187 
188     QVector<const AttributeRelation *> relations;
189     QList<PortRelationDescriptor *> portRelations;
190     QList<SlotRelationDescriptor *> slotRelations;
191 
192 };  // Attribute
193 
194 // getAttributeValue function realizations with scripting support
195 template<>
getAttributeValue(Workflow::WorkflowContext * ctx)196 inline QString Attribute::getAttributeValue(Workflow::WorkflowContext *ctx) const {
197     if (scriptData.isEmpty()) {
198         return getAttributeValueWithoutScript<QString>();
199     }
200     // run script
201     WorkflowScriptEngine engine(ctx);
202     QMap<QString, QScriptValue> scriptVars;
203     foreach (const Descriptor &key, scriptData.getScriptVars().uniqueKeys()) {
204         assert(!key.getId().isEmpty());
205         scriptVars[key.getId()] = engine.newVariant(scriptData.getScriptVars().value(key));
206     }
207 
208     TaskStateInfo tsi;
209     WorkflowScriptLibrary::initEngine(&engine);
210     QScriptValue scriptResult = ScriptTask::runScript(&engine, scriptVars, scriptData.getScriptText(), tsi);
211 
212     // FIXME: report errors!
213     // FIXME: write to log
214     if (tsi.cancelFlag) {
215         if (!tsi.hasError()) {
216             tsi.setError("Script task canceled");
217         }
218     }
219     if (tsi.hasError()) {
220         scriptLog.error(tsi.getError());
221         return QString();
222     }
223     if (scriptResult.isString()) {
224         return scriptResult.toString();
225     }
226 
227     return QString();
228 }
229 
230 template<>
getAttributeValue(Workflow::WorkflowContext * ctx)231 inline int Attribute::getAttributeValue(Workflow::WorkflowContext *ctx) const {
232     if (scriptData.isEmpty()) {
233         return getAttributeValueWithoutScript<int>();
234     }
235 
236     WorkflowScriptEngine engine(ctx);
237     QMap<QString, QScriptValue> scriptVars;
238     foreach (const Descriptor &key, scriptData.getScriptVars().uniqueKeys()) {
239         assert(!key.getId().isEmpty());
240         scriptVars[key.getId()] = engine.newVariant(scriptData.getScriptVars().value(key));
241     }
242 
243     TaskStateInfo tsi;
244     WorkflowScriptLibrary::initEngine(&engine);
245     QScriptValue scriptResult = ScriptTask::runScript(&engine, scriptVars, scriptData.getScriptText(), tsi);
246 
247     if (tsi.cancelFlag) {
248         if (!tsi.hasError()) {
249             tsi.setError("Script task canceled");
250         }
251     }
252     if (tsi.hasError()) {
253         scriptLog.error(tsi.getError());
254         return 0;
255     }
256     if (scriptResult.isNumber()) {
257         return scriptResult.toInt32();
258     }
259 
260     return 0;
261 }
262 
263 }  // namespace U2
264 
265 Q_DECLARE_METATYPE(U2::AttributeScript)
266 Q_DECLARE_OPERATORS_FOR_FLAGS(U2::Attribute::Flags)
267 
268 #endif
269