| /**************************************************************************** |
| ** |
| ** 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> ¶meters) |
| : invokeInfo(invokeInfo) |
| , names(namelist) |
| , parameters(parameters) |
| {} |
| |
| QScxmlStaticScxmlServiceFactoryPrivate::QScxmlStaticScxmlServiceFactoryPrivate( |
| const QMetaObject *metaObject, |
| const QScxmlExecutableContent::InvokeInfo &invokeInfo, |
| const QVector<QScxmlExecutableContent::StringId> &names, |
| const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters) |
| : 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> ¶meters, |
| 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> ¶meters, |
| 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 ¶m : 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> ¶meters, |
| 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> ¶meters, |
| 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 |