blob: 912be00d6b2d6f8d8014f272c6021c817182ed74 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtScxml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qscxmlglobals_p.h"
#include "qscxmlinvokableservice_p.h"
#include "qscxmlstatemachine_p.h"
QT_BEGIN_NAMESPACE
/*!
* \class QScxmlInvokableService
* \brief The QScxmlInvokableService class is the base class for services called
* from state machines.
* \since 5.8
* \inmodule QtScxml
*
* The services are called from state machines via the mechanism described in
* \l {SCXML Specification - 6.4 <invoke>}. This class represents an actual
* instance of an invoked service.
*/
/*!
* \class QScxmlInvokableServiceFactory
* \brief The QScxmlInvokableServiceFactory class creates invokable service
* instances.
* \since 5.8
* \inmodule QtScxml
*
* Each service instance represents
* an \c <invoke> element in the SCXML document. Each time the service is
* actually invoked, a new instance of QScxmlInvokableService is created.
*/
/*!
\property QScxmlInvokableServiceFactory::invokeInfo
\brief The QScxmlExecutableContent::InvokeInfo passed to the constructor.
*/
/*!
\property QScxmlInvokableServiceFactory::names
\brief The names passed to the constructor.
*/
/*!
\property QScxmlInvokableServiceFactory::parameters
\brief The parameters passed to the constructor.
*/
/*!
* \class QScxmlStaticScxmlServiceFactory
* \brief The QScxmlStaticScxmlServiceFactory class creates SCXML service
* instances from precompiled documents.
* \since 5.8
* \inmodule QtScxml
*
* A factory for instantiating SCXML state machines from files known at compile
* time, that is, files specified via the \c src attribute in \c <invoke>.
*/
/*!
* \class QScxmlDynamicScxmlServiceFactory
* \brief The QScxmlDynamicScxmlServiceFactory class creates SCXML service
* instances from documents loaded at runtime.
* \since 5.8
* \inmodule QtScxml
*
* Dynamically resolved services are used when loading \l{SCXML Specification}
* {SCXML} content from files that a
* parent state machine requests at runtime, via the \c srcexpr attribute in
* the \c <invoke> element.
*/
/*!
* \property QScxmlInvokableService::parentStateMachine
*
* \brief The SCXML state machine that invoked the service.
*/
/*!
* \property QScxmlInvokableService::id
*
* \brief The ID of the invokable service.
*
* The ID is specified by the \c id attribute of the \c <invoke> element.
*/
/*!
* \property QScxmlInvokableService::name
*
* \brief The name of the service being invoked.
*/
/*!
* \fn QScxmlInvokableService::postEvent(QScxmlEvent *event)
*
* Sends an \a event to the service.
*/
/*!
* \fn QScxmlInvokableService::start()
*
* Starts the invokable service. Returns \c true on success, or \c false if the
* invocation fails.
*/
/*!
* \fn QScxmlInvokableServiceFactory::invoke(QScxmlStateMachine *parentStateMachine)
*
* Invokes the service with the parameters given in the constructor, passing
* \a parentStateMachine as the parent. Returns the new invokable service.
*/
QScxmlInvokableServicePrivate::QScxmlInvokableServicePrivate(QScxmlStateMachine *parentStateMachine)
: parentStateMachine(parentStateMachine)
{
static int metaType = qRegisterMetaType<QScxmlInvokableService *>();
Q_UNUSED(metaType);
}
QScxmlInvokableServiceFactoryPrivate::QScxmlInvokableServiceFactoryPrivate(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
const QVector<QScxmlExecutableContent::StringId> &namelist,
const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
: invokeInfo(invokeInfo)
, names(namelist)
, parameters(parameters)
{}
QScxmlStaticScxmlServiceFactoryPrivate::QScxmlStaticScxmlServiceFactoryPrivate(
const QMetaObject *metaObject,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
const QVector<QScxmlExecutableContent::StringId> &names,
const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
: QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters), metaObject(metaObject)
{
}
QScxmlInvokableService::QScxmlInvokableService(QScxmlStateMachine *parentStateMachine,
QScxmlInvokableServiceFactory *factory) :
QObject(*(new QScxmlInvokableServicePrivate(parentStateMachine)), factory)
{
}
QScxmlStateMachine *QScxmlInvokableService::parentStateMachine() const
{
Q_D(const QScxmlInvokableService);
return d->parentStateMachine;
}
QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
const QVector<QScxmlExecutableContent::StringId> &names,
const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent)
: QObject(*(new QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters)), parent)
{}
const QScxmlExecutableContent::InvokeInfo &QScxmlInvokableServiceFactory::invokeInfo() const
{
Q_D(const QScxmlInvokableServiceFactory);
return d->invokeInfo;
}
const QVector<QScxmlExecutableContent::ParameterInfo> &
QScxmlInvokableServiceFactory::parameters() const
{
Q_D(const QScxmlInvokableServiceFactory);
return d->parameters;
}
const QVector<QScxmlExecutableContent::StringId> &QScxmlInvokableServiceFactory::names() const
{
Q_D(const QScxmlInvokableServiceFactory);
return d->names;
}
QString calculateSrcexpr(QScxmlStateMachine *parent, QScxmlExecutableContent::EvaluatorId srcexpr,
bool *ok)
{
Q_ASSERT(ok);
*ok = true;
auto dataModel = parent->dataModel();
if (srcexpr != QScxmlExecutableContent::NoEvaluator) {
*ok = false;
auto v = dataModel->evaluateToString(srcexpr, ok);
if (!*ok)
return QString();
return v;
}
return QString();
}
QString QScxmlInvokableServicePrivate::calculateId(
QScxmlStateMachine *parent, const QScxmlExecutableContent::InvokeInfo &invokeInfo,
bool *ok) const
{
Q_ASSERT(ok);
*ok = true;
auto stateMachine = parent->tableData();
if (invokeInfo.id != QScxmlExecutableContent::NoString) {
return stateMachine->string(invokeInfo.id);
}
const QString newId = QScxmlStateMachinePrivate::generateSessionId(
stateMachine->string(invokeInfo.prefix));
if (invokeInfo.location != QScxmlExecutableContent::NoString) {
auto idloc = stateMachine->string(invokeInfo.location);
auto ctxt = stateMachine->string(invokeInfo.context);
*ok = parent->dataModel()->setScxmlProperty(idloc, newId, ctxt);
if (!*ok)
return QString();
}
return newId;
}
QVariantMap QScxmlInvokableServicePrivate::calculateData(
QScxmlStateMachine *parent,
const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
const QVector<QScxmlExecutableContent::StringId> &names,
bool *ok) const
{
Q_ASSERT(ok);
QVariantMap result;
auto dataModel = parent->dataModel();
auto tableData = parent->tableData();
for (const QScxmlExecutableContent::ParameterInfo &param : parameters) {
auto name = tableData->string(param.name);
if (param.expr != QScxmlExecutableContent::NoEvaluator) {
*ok = false;
auto v = dataModel->evaluateToVariant(param.expr, ok);
if (!*ok)
return QVariantMap();
result.insert(name, v);
} else {
QString loc;
if (param.location != QScxmlExecutableContent::NoString) {
loc = tableData->string(param.location);
}
if (loc.isEmpty()) {
// TODO: error message?
*ok = false;
return QVariantMap();
}
auto v = dataModel->scxmlProperty(loc);
result.insert(name, v);
}
}
for (QScxmlExecutableContent::StringId locid : names) {
QString loc;
if (locid != QScxmlExecutableContent::NoString) {
loc = tableData->string(locid);
}
if (loc.isEmpty()) {
// TODO: error message?
*ok = false;
return QVariantMap();
}
if (dataModel->hasScxmlProperty(loc)) {
auto v = dataModel->scxmlProperty(loc);
result.insert(loc, v);
} else {
*ok = false;
return QVariantMap();
}
}
return result;
}
QScxmlScxmlService::~QScxmlScxmlService()
{
delete m_stateMachine;
}
/*!
Creates a SCXML service wrapping \a stateMachine, invoked from
\a parentStateMachine, as a child of \a factory.
*/
QScxmlScxmlService::QScxmlScxmlService(QScxmlStateMachine *stateMachine,
QScxmlStateMachine *parentStateMachine,
QScxmlInvokableServiceFactory *factory)
: QScxmlInvokableService(parentStateMachine, factory), m_stateMachine(stateMachine)
{
QScxmlStateMachinePrivate::get(stateMachine)->m_parentStateMachine = parentStateMachine;
}
/*!
* \reimp
*/
bool QScxmlScxmlService::start()
{
Q_D(QScxmlInvokableService);
qCDebug(qscxmlLog) << parentStateMachine() << "preparing to start" << m_stateMachine;
const QScxmlInvokableServiceFactory *factory
= qobject_cast<QScxmlInvokableServiceFactory *>(parent());
Q_ASSERT(factory);
bool ok = false;
auto id = d->calculateId(parentStateMachine(), factory->invokeInfo(), &ok);
if (!ok)
return false;
auto data = d->calculateData(parentStateMachine(), factory->parameters(), factory->names(),
&ok);
if (!ok)
return false;
QScxmlStateMachinePrivate::get(m_stateMachine)->m_sessionId = id;
m_stateMachine->setInitialValues(data);
if (m_stateMachine->init()) {
qCDebug(qscxmlLog) << parentStateMachine() << "starting" << m_stateMachine;
m_stateMachine->start();
return true;
}
qCDebug(qscxmlLog) << parentStateMachine() << "failed to start" << m_stateMachine;
return false;
}
/*!
\reimp
*/
QString QScxmlScxmlService::id() const
{
return m_stateMachine->sessionId();
}
/*!
\reimp
*/
QString QScxmlScxmlService::name() const
{
return m_stateMachine->name();
}
/*!
\reimp
*/
void QScxmlScxmlService::postEvent(QScxmlEvent *event)
{
QScxmlStateMachinePrivate::get(m_stateMachine)->postEvent(event);
}
QScxmlStateMachine *QScxmlScxmlService::stateMachine() const
{
return m_stateMachine;
}
/*!
Creates a factory for dynamically resolved services, passing the attributes of
the \c <invoke> element as \a invokeInfo, any \c <param> child elements as
\a parameters, the content of the \c names attribute as \a names, and the
QObject parent \a parent.
*/
QScxmlDynamicScxmlServiceFactory::QScxmlDynamicScxmlServiceFactory(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
const QVector<QScxmlExecutableContent::StringId> &names,
const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent)
: QScxmlInvokableServiceFactory(invokeInfo, names, parameters, parent)
{}
/*!
\reimp
*/
QScxmlInvokableService *QScxmlDynamicScxmlServiceFactory::invoke(
QScxmlStateMachine *parentStateMachine)
{
bool ok = true;
auto srcexpr = calculateSrcexpr(parentStateMachine, invokeInfo().expr, &ok);
if (!ok)
return nullptr;
return invokeDynamicScxmlService(srcexpr, parentStateMachine, this);
}
QScxmlStaticScxmlServiceFactory::QScxmlStaticScxmlServiceFactory(
const QMetaObject *metaObject,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
const QVector<QScxmlExecutableContent::StringId> &nameList,
const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent)
: QScxmlInvokableServiceFactory(*(new QScxmlStaticScxmlServiceFactoryPrivate(
metaObject, invokeInfo, nameList, parameters)), parent)
{
}
/*!
\reimp
*/
QScxmlInvokableService *QScxmlStaticScxmlServiceFactory::invoke(
QScxmlStateMachine *parentStateMachine)
{
Q_D(const QScxmlStaticScxmlServiceFactory);
QScxmlStateMachine *instance = qobject_cast<QScxmlStateMachine *>(
d->metaObject->newInstance(Q_ARG(QObject *, this)));
return instance ? invokeStaticScxmlService(instance, parentStateMachine, this) : nullptr;
}
QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
QScxmlInvokableServiceFactoryPrivate &dd, QObject *parent)
: QObject(dd, parent)
{}
QScxmlScxmlService *invokeStaticScxmlService(QScxmlStateMachine *childStateMachine,
QScxmlStateMachine *parentStateMachine,
QScxmlInvokableServiceFactory *factory)
{
QScxmlStateMachinePrivate::get(childStateMachine)->setIsInvoked(true);
return new QScxmlScxmlService(childStateMachine, parentStateMachine, factory);
}
QT_END_NAMESPACE