1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtScxml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qscxmlglobals_p.h"
41#include "qscxmlinvokableservice_p.h"
42#include "qscxmlstatemachine_p.h"
43
44QT_BEGIN_NAMESPACE
45
46/*!
47 * \class QScxmlInvokableService
48 * \brief The QScxmlInvokableService class is the base class for services called
49 * from state machines.
50 * \since 5.8
51 * \inmodule QtScxml
52 *
53 * The services are called from state machines via the mechanism described in
54 * \l {SCXML Specification - 6.4 <invoke>}. This class represents an actual
55 * instance of an invoked service.
56 */
57
58/*!
59 * \class QScxmlInvokableServiceFactory
60 * \brief The QScxmlInvokableServiceFactory class creates invokable service
61 * instances.
62 * \since 5.8
63 * \inmodule QtScxml
64 *
65 * Each service instance represents
66 * an \c <invoke> element in the SCXML document. Each time the service is
67 * actually invoked, a new instance of QScxmlInvokableService is created.
68 */
69
70/*!
71 \property QScxmlInvokableServiceFactory::invokeInfo
72
73 \brief The QScxmlExecutableContent::InvokeInfo passed to the constructor.
74 */
75
76/*!
77 \property QScxmlInvokableServiceFactory::names
78
79 \brief The names passed to the constructor.
80 */
81
82/*!
83 \property QScxmlInvokableServiceFactory::parameters
84
85 \brief The parameters passed to the constructor.
86 */
87
88/*!
89 * \class QScxmlStaticScxmlServiceFactory
90 * \brief The QScxmlStaticScxmlServiceFactory class creates SCXML service
91 * instances from precompiled documents.
92 * \since 5.8
93 * \inmodule QtScxml
94 *
95 * A factory for instantiating SCXML state machines from files known at compile
96 * time, that is, files specified via the \c src attribute in \c <invoke>.
97 */
98
99/*!
100 * \class QScxmlDynamicScxmlServiceFactory
101 * \brief The QScxmlDynamicScxmlServiceFactory class creates SCXML service
102 * instances from documents loaded at runtime.
103 * \since 5.8
104 * \inmodule QtScxml
105 *
106 * Dynamically resolved services are used when loading \l{SCXML Specification}
107 * {SCXML} content from files that a
108 * parent state machine requests at runtime, via the \c srcexpr attribute in
109 * the \c <invoke> element.
110 */
111
112/*!
113 * \property QScxmlInvokableService::parentStateMachine
114 *
115 * \brief The SCXML state machine that invoked the service.
116 */
117
118/*!
119 * \property QScxmlInvokableService::id
120 *
121 * \brief The ID of the invokable service.
122 *
123 * The ID is specified by the \c id attribute of the \c <invoke> element.
124 */
125
126/*!
127 * \property QScxmlInvokableService::name
128 *
129 * \brief The name of the service being invoked.
130 */
131
132/*!
133 * \fn QScxmlInvokableService::postEvent(QScxmlEvent *event)
134 *
135 * Sends an \a event to the service.
136 */
137
138/*!
139 * \fn QScxmlInvokableService::start()
140 *
141 * Starts the invokable service. Returns \c true on success, or \c false if the
142 * invocation fails.
143 */
144
145/*!
146 * \fn QScxmlInvokableServiceFactory::invoke(QScxmlStateMachine *parentStateMachine)
147 *
148 * Invokes the service with the parameters given in the constructor, passing
149 * \a parentStateMachine as the parent. Returns the new invokable service.
150 */
151
152QScxmlInvokableServicePrivate::QScxmlInvokableServicePrivate(QScxmlStateMachine *parentStateMachine)
153 : parentStateMachine(parentStateMachine)
154{
155 static int metaType = qRegisterMetaType<QScxmlInvokableService *>();
156 Q_UNUSED(metaType);
157}
158
159QScxmlInvokableServiceFactoryPrivate::QScxmlInvokableServiceFactoryPrivate(
160 const QScxmlExecutableContent::InvokeInfo &invokeInfo,
161 const QVector<QScxmlExecutableContent::StringId> &namelist,
162 const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
163 : invokeInfo(invokeInfo)
164 , names(namelist)
165 , parameters(parameters)
166{}
167
168QScxmlStaticScxmlServiceFactoryPrivate::QScxmlStaticScxmlServiceFactoryPrivate(
169 const QMetaObject *metaObject,
170 const QScxmlExecutableContent::InvokeInfo &invokeInfo,
171 const QVector<QScxmlExecutableContent::StringId> &names,
172 const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
173 : QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters), metaObject(metaObject)
174{
175}
176
177QScxmlInvokableService::QScxmlInvokableService(QScxmlStateMachine *parentStateMachine,
178 QScxmlInvokableServiceFactory *factory) :
179 QObject(*(new QScxmlInvokableServicePrivate(parentStateMachine)), factory)
180{
181}
182
183QScxmlStateMachine *QScxmlInvokableService::parentStateMachine() const
184{
185 Q_D(const QScxmlInvokableService);
186 return d->parentStateMachine;
187}
188
189QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
190 const QScxmlExecutableContent::InvokeInfo &invokeInfo,
191 const QVector<QScxmlExecutableContent::StringId> &names,
192 const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
193 QObject *parent)
194 : QObject(*(new QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters)), parent)
195{}
196
197const QScxmlExecutableContent::InvokeInfo &QScxmlInvokableServiceFactory::invokeInfo() const
198{
199 Q_D(const QScxmlInvokableServiceFactory);
200 return d->invokeInfo;
201}
202
203const QVector<QScxmlExecutableContent::ParameterInfo> &
204QScxmlInvokableServiceFactory::parameters() const
205{
206 Q_D(const QScxmlInvokableServiceFactory);
207 return d->parameters;
208}
209
210const QVector<QScxmlExecutableContent::StringId> &QScxmlInvokableServiceFactory::names() const
211{
212 Q_D(const QScxmlInvokableServiceFactory);
213 return d->names;
214}
215
216QString calculateSrcexpr(QScxmlStateMachine *parent, QScxmlExecutableContent::EvaluatorId srcexpr,
217 bool *ok)
218{
219 Q_ASSERT(ok);
220 *ok = true;
221 auto dataModel = parent->dataModel();
222
223 if (srcexpr != QScxmlExecutableContent::NoEvaluator) {
224 *ok = false;
225 auto v = dataModel->evaluateToString(id: srcexpr, ok);
226 if (!*ok)
227 return QString();
228 return v;
229 }
230
231 return QString();
232}
233
234QString QScxmlInvokableServicePrivate::calculateId(
235 QScxmlStateMachine *parent, const QScxmlExecutableContent::InvokeInfo &invokeInfo,
236 bool *ok) const
237{
238 Q_ASSERT(ok);
239 *ok = true;
240 auto stateMachine = parent->tableData();
241
242 if (invokeInfo.id != QScxmlExecutableContent::NoString) {
243 return stateMachine->string(id: invokeInfo.id);
244 }
245
246 const QString newId = QScxmlStateMachinePrivate::generateSessionId(
247 prefix: stateMachine->string(id: invokeInfo.prefix));
248
249 if (invokeInfo.location != QScxmlExecutableContent::NoString) {
250 auto idloc = stateMachine->string(id: invokeInfo.location);
251 auto ctxt = stateMachine->string(id: invokeInfo.context);
252 *ok = parent->dataModel()->setScxmlProperty(name: idloc, value: newId, context: ctxt);
253 if (!*ok)
254 return QString();
255 }
256
257 return newId;
258}
259
260QVariantMap QScxmlInvokableServicePrivate::calculateData(
261 QScxmlStateMachine *parent,
262 const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
263 const QVector<QScxmlExecutableContent::StringId> &names,
264 bool *ok) const
265{
266 Q_ASSERT(ok);
267
268 QVariantMap result;
269 auto dataModel = parent->dataModel();
270 auto tableData = parent->tableData();
271
272 for (const QScxmlExecutableContent::ParameterInfo &param : parameters) {
273 auto name = tableData->string(id: param.name);
274
275 if (param.expr != QScxmlExecutableContent::NoEvaluator) {
276 *ok = false;
277 auto v = dataModel->evaluateToVariant(id: param.expr, ok);
278 if (!*ok)
279 return QVariantMap();
280 result.insert(akey: name, avalue: v);
281 } else {
282 QString loc;
283 if (param.location != QScxmlExecutableContent::NoString) {
284 loc = tableData->string(id: param.location);
285 }
286
287 if (loc.isEmpty()) {
288 // TODO: error message?
289 *ok = false;
290 return QVariantMap();
291 }
292
293 auto v = dataModel->scxmlProperty(name: loc);
294 result.insert(akey: name, avalue: v);
295 }
296 }
297
298 for (QScxmlExecutableContent::StringId locid : names) {
299 QString loc;
300 if (locid != QScxmlExecutableContent::NoString) {
301 loc = tableData->string(id: locid);
302 }
303 if (loc.isEmpty()) {
304 // TODO: error message?
305 *ok = false;
306 return QVariantMap();
307 }
308 if (dataModel->hasScxmlProperty(name: loc)) {
309 auto v = dataModel->scxmlProperty(name: loc);
310 result.insert(akey: loc, avalue: v);
311 } else {
312 *ok = false;
313 return QVariantMap();
314 }
315 }
316
317 return result;
318}
319
320QScxmlScxmlService::~QScxmlScxmlService()
321{
322 delete m_stateMachine;
323}
324
325/*!
326 Creates a SCXML service wrapping \a stateMachine, invoked from
327 \a parentStateMachine, as a child of \a factory.
328 */
329QScxmlScxmlService::QScxmlScxmlService(QScxmlStateMachine *stateMachine,
330 QScxmlStateMachine *parentStateMachine,
331 QScxmlInvokableServiceFactory *factory)
332 : QScxmlInvokableService(parentStateMachine, factory), m_stateMachine(stateMachine)
333{
334 QScxmlStateMachinePrivate::get(t: stateMachine)->m_parentStateMachine = parentStateMachine;
335}
336
337/*!
338 * \reimp
339 */
340bool QScxmlScxmlService::start()
341{
342 Q_D(QScxmlInvokableService);
343 qCDebug(qscxmlLog) << parentStateMachine() << "preparing to start" << m_stateMachine;
344
345 const QScxmlInvokableServiceFactory *factory
346 = qobject_cast<QScxmlInvokableServiceFactory *>(object: parent());
347 Q_ASSERT(factory);
348
349 bool ok = false;
350 auto id = d->calculateId(parent: parentStateMachine(), invokeInfo: factory->invokeInfo(), ok: &ok);
351 if (!ok)
352 return false;
353 auto data = d->calculateData(parent: parentStateMachine(), parameters: factory->parameters(), names: factory->names(),
354 ok: &ok);
355 if (!ok)
356 return false;
357
358 QScxmlStateMachinePrivate::get(t: m_stateMachine)->m_sessionId = id;
359 m_stateMachine->setInitialValues(data);
360 if (m_stateMachine->init()) {
361 qCDebug(qscxmlLog) << parentStateMachine() << "starting" << m_stateMachine;
362 m_stateMachine->start();
363 return true;
364 }
365
366 qCDebug(qscxmlLog) << parentStateMachine() << "failed to start" << m_stateMachine;
367 return false;
368}
369
370/*!
371 \reimp
372 */
373QString QScxmlScxmlService::id() const
374{
375 return m_stateMachine->sessionId();
376}
377
378/*!
379 \reimp
380 */
381QString QScxmlScxmlService::name() const
382{
383 return m_stateMachine->name();
384}
385
386/*!
387 \reimp
388 */
389void QScxmlScxmlService::postEvent(QScxmlEvent *event)
390{
391 QScxmlStateMachinePrivate::get(t: m_stateMachine)->postEvent(event);
392}
393
394QScxmlStateMachine *QScxmlScxmlService::stateMachine() const
395{
396 return m_stateMachine;
397}
398
399/*!
400 Creates a factory for dynamically resolved services, passing the attributes of
401 the \c <invoke> element as \a invokeInfo, any \c <param> child elements as
402 \a parameters, the content of the \c names attribute as \a names, and the
403 QObject parent \a parent.
404 */
405QScxmlDynamicScxmlServiceFactory::QScxmlDynamicScxmlServiceFactory(
406 const QScxmlExecutableContent::InvokeInfo &invokeInfo,
407 const QVector<QScxmlExecutableContent::StringId> &names,
408 const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
409 QObject *parent)
410 : QScxmlInvokableServiceFactory(invokeInfo, names, parameters, parent)
411{}
412
413/*!
414 \reimp
415 */
416QScxmlInvokableService *QScxmlDynamicScxmlServiceFactory::invoke(
417 QScxmlStateMachine *parentStateMachine)
418{
419 bool ok = true;
420 auto srcexpr = calculateSrcexpr(parent: parentStateMachine, srcexpr: invokeInfo().expr, ok: &ok);
421 if (!ok)
422 return nullptr;
423
424 return invokeDynamicScxmlService(sourceUrl: srcexpr, parentStateMachine, factory: this);
425}
426
427QScxmlStaticScxmlServiceFactory::QScxmlStaticScxmlServiceFactory(
428 const QMetaObject *metaObject,
429 const QScxmlExecutableContent::InvokeInfo &invokeInfo,
430 const QVector<QScxmlExecutableContent::StringId> &nameList,
431 const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
432 QObject *parent)
433 : QScxmlInvokableServiceFactory(*(new QScxmlStaticScxmlServiceFactoryPrivate(
434 metaObject, invokeInfo, nameList, parameters)), parent)
435{
436}
437
438/*!
439 \reimp
440 */
441QScxmlInvokableService *QScxmlStaticScxmlServiceFactory::invoke(
442 QScxmlStateMachine *parentStateMachine)
443{
444 Q_D(const QScxmlStaticScxmlServiceFactory);
445 QScxmlStateMachine *instance = qobject_cast<QScxmlStateMachine *>(
446 object: d->metaObject->newInstance(Q_ARG(QObject *, this)));
447 return instance ? invokeStaticScxmlService(childStateMachine: instance, parentStateMachine, factory: this) : nullptr;
448}
449
450QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
451 QScxmlInvokableServiceFactoryPrivate &dd, QObject *parent)
452 : QObject(dd, parent)
453{}
454
455QScxmlScxmlService *invokeStaticScxmlService(QScxmlStateMachine *childStateMachine,
456 QScxmlStateMachine *parentStateMachine,
457 QScxmlInvokableServiceFactory *factory)
458{
459 QScxmlStateMachinePrivate::get(t: childStateMachine)->setIsInvoked(true);
460 return new QScxmlScxmlService(childStateMachine, parentStateMachine, factory);
461}
462
463QT_END_NAMESPACE
464

source code of qtscxml/src/scxml/qscxmlinvokableservice.cpp