diff options
35 files changed, 968 insertions, 394 deletions
diff --git a/examples/scxml/trafficlight-common/TrafficLight.qml b/examples/scxml/trafficlight-common/TrafficLight.qml index 1da3726..ec11ed5 100644 --- a/examples/scxml/trafficlight-common/TrafficLight.qml +++ b/examples/scxml/trafficlight-common/TrafficLight.qml @@ -60,6 +60,8 @@ Window { visible: true width: lights.width height: lights.height + maximumWidth: lights.implicitWidth + maximumHeight: lights.implicitHeight Lights { id: lights diff --git a/src/scxml/doc/external-resources.qdoc b/src/scxml/doc/external-resources.qdoc index 3cbdc6f..39a1c3b 100644 --- a/src/scxml/doc/external-resources.qdoc +++ b/src/scxml/doc/external-resources.qdoc @@ -45,6 +45,11 @@ */ /*! + \externalpage https://www.w3.org/TR/scxml/#invoke + \title SCXML Specification - 6.4 <invoke> +*/ + +/*! \externalpage https://www.w3.org/TR/scxml/#minimal-profile \title SCXML Specification - B.1 The Null Data Model */ diff --git a/src/scxml/qscxmlcompiler.cpp b/src/scxml/qscxmlcompiler.cpp index 691f87a..b8434c6 100644 --- a/src/scxml/qscxmlcompiler.cpp +++ b/src/scxml/qscxmlcompiler.cpp @@ -473,13 +473,14 @@ private: }; #ifndef BUILD_QSCXMLC -class InvokeDynamicScxmlFactory: public QScxmlScxmlServiceFactory +class InvokeDynamicScxmlFactory: public QScxmlInvokableServiceFactory { + Q_OBJECT public: InvokeDynamicScxmlFactory(const QScxmlExecutableContent::InvokeInfo &invokeInfo, const QVector<QScxmlExecutableContent::StringId> &namelist, const QVector<QScxmlExecutableContent::ParameterInfo> ¶ms) - : QScxmlScxmlServiceFactory(invokeInfo, namelist, params) + : QScxmlInvokableServiceFactory(invokeInfo, namelist, params) {} void setContent(const QSharedPointer<DocumentModel::ScxmlDocument> &content) @@ -647,12 +648,12 @@ inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke( QScxmlStateMachine *parentStateMachine) { bool ok = true; - auto srcexpr = d->calculateSrcexpr(parentStateMachine, &ok); + auto srcexpr = calculateSrcexpr(parentStateMachine, invokeInfo().expr, &ok); if (!ok) return Q_NULLPTR; if (!srcexpr.isEmpty()) - return invokeDynamic(parentStateMachine, srcexpr); + return invokeDynamicScxmlService(srcexpr, parentStateMachine, this); auto childStateMachine = DynamicStateMachine::build(m_content.data()); @@ -660,15 +661,16 @@ inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke( dm->setParent(childStateMachine); childStateMachine->setDataModel(dm); - return invokeStatic(childStateMachine, parentStateMachine); + return invokeStaticScxmlService(childStateMachine, parentStateMachine, this); } #endif // BUILD_QSCXMLC } // anonymous namespace #ifndef BUILD_QSCXMLC -QScxmlScxmlService *QScxmlScxmlServiceFactory::invokeDynamic( - QScxmlStateMachine *parentStateMachine, const QString &sourceUrl) +QScxmlScxmlService *invokeDynamicScxmlService(const QString &sourceUrl, + QScxmlStateMachine *parentStateMachine, + QScxmlInvokableServiceFactory *factory) { QScxmlCompiler::Loader *loader = parentStateMachine->loader(); @@ -708,7 +710,7 @@ QScxmlScxmlService *QScxmlScxmlServiceFactory::invokeDynamic( dm->setParent(childStateMachine); childStateMachine->setDataModel(dm); - return invokeStatic(childStateMachine, parentStateMachine); + return invokeStaticScxmlService(childStateMachine, parentStateMachine, factory); } #endif // BUILD_QSCXMLC @@ -2483,3 +2485,7 @@ QScxmlCompilerPrivate::ParserState::ParserState(QScxmlCompilerPrivate::ParserSta {} QT_END_NAMESPACE + +#ifndef BUILD_QSCXMLC +#include "qscxmlcompiler.moc" +#endif diff --git a/src/scxml/qscxmlcompiler_p.h b/src/scxml/qscxmlcompiler_p.h index 6909c2c..f74e282 100644 --- a/src/scxml/qscxmlcompiler_p.h +++ b/src/scxml/qscxmlcompiler_p.h @@ -387,6 +387,7 @@ struct Scxml: public StateContainer, public Node : Node(xmlLocation) , dataModel(NullDataModel) , binding(EarlyBinding) + , initialTransition(nullptr) {} void add(StateOrTransition *s) Q_DECL_OVERRIDE diff --git a/src/scxml/qscxmlcppdatamodel.cpp b/src/scxml/qscxmlcppdatamodel.cpp index bc09d65..ba18cc7 100644 --- a/src/scxml/qscxmlcppdatamodel.cpp +++ b/src/scxml/qscxmlcppdatamodel.cpp @@ -52,7 +52,7 @@ using namespace QScxmlExecutableContent; \sa QScxmlStateMachine QScxmlDataModel - The C++ data model for SCXML that lets you write C++ code for \e expr attributes and \c <script> + The C++ data model for SCXML lets you write C++ code for \e expr attributes and \c <script> elements. The \e {data part} of the data model is backed by a subclass of QScxmlCppDataModel, for which the Qt SCXML compiler (\c qscxmlc) will generate the dispatch methods. It cannot be used when loading an SCXML file at runtime. @@ -69,6 +69,7 @@ using namespace QScxmlExecutableContent; class TheDataModel: public QScxmlCppDataModel { + Q_OBJECT Q_SCXML_DATAMODEL }; \endcode @@ -77,8 +78,6 @@ class TheDataModel: public QScxmlCppDataModel This macro expands to the declaration of some virtual methods whose implementation is generated by the Qt SCXML compiler. - \note You can of course inherit from both QScxmlCppDataModel and QObject. - The Qt SCXML compiler will generate the various \c evaluateTo methods, and convert expressions and scripts into lambdas inside those methods. For example: \code @@ -134,11 +133,6 @@ QScxmlCppDataModel::QScxmlCppDataModel(QObject *parent) : QScxmlDataModel(*(new QScxmlCppDataModelPrivate), parent) {} -/*! \internal */ -QScxmlCppDataModel::~QScxmlCppDataModel() -{ -} - /*! * Called during state machine initialization to set up a state machine using the initial values * for data model variables specified by their keys, \a initialDataValues. These @@ -153,33 +147,53 @@ bool QScxmlCppDataModel::setup(const QVariantMap &initialDataValues) return true; } -void QScxmlCppDataModel::evaluateAssignment(EvaluatorId id, bool *ok) +/*! + \reimp + + This method does not perform any action, ignores \a id, and sets \a ok to + \c false. Override it in your specific data model in order to implement + \c <assign>. + */ +void QScxmlCppDataModel::evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_UNUSED(id); - Q_UNUSED(ok); - Q_UNREACHABLE(); + *ok = false; } -void QScxmlCppDataModel::evaluateInitialization(EvaluatorId id, bool *ok) +/*! + \reimp + + This method does not perform any action, ignores \a id, and sets \a ok to + \c false. Override it in your specific data model in order to implement + \c <data>. + */ +void QScxmlCppDataModel::evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_UNUSED(id); - Q_UNUSED(ok); - Q_UNREACHABLE(); + *ok = false; } -bool QScxmlCppDataModel::evaluateForeach(EvaluatorId id, bool *ok, ForeachLoopBody *body) +/*! + \reimp + + This method does not perform any action, ignores \a id and \a body, and sets + \a ok to \c false. Override it in your specific data model in order to + implement \c <foreach>. + */ +void QScxmlCppDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, + ForeachLoopBody *body) { Q_UNUSED(id); - Q_UNUSED(ok); Q_UNUSED(body); - Q_UNREACHABLE(); - return false; + *ok = false; } /*! - * Sets the \a event that will be processed next. - * - * \sa QScxmlCppDataModel::scxmlEvent + \reimp + + Sets the \a event that will be processed next. + + \sa QScxmlCppDataModel::scxmlEvent */ void QScxmlCppDataModel::setScxmlEvent(const QScxmlEvent &event) { @@ -206,7 +220,11 @@ const QScxmlEvent &QScxmlCppDataModel::scxmlEvent() const } /*! - * \reimp + \reimp + + This method always returns an empty QVariant and ignores \a name. + Override it to implement the lookup of data model properties via the + \c location attribute of various elements. */ QVariant QScxmlCppDataModel::scxmlProperty(const QString &name) const { @@ -215,7 +233,11 @@ QVariant QScxmlCppDataModel::scxmlProperty(const QString &name) const } /*! - * \reimp + \reimp + + This method always returns \c false and ignores \a name. + Override it to implement the lookup of data model properties via the + \c location attribute of various elements. */ bool QScxmlCppDataModel::hasScxmlProperty(const QString &name) const { @@ -224,20 +246,25 @@ bool QScxmlCppDataModel::hasScxmlProperty(const QString &name) const } /*! - * \reimp + \reimp + + This method always returns \c false and ignores \a name, \a value, and + \a context. + Override it to implement the lookup of data model properties via the + \c location attribute of various elements. */ -bool QScxmlCppDataModel::setScxmlProperty(const QString &name, const QVariant &value, const QString &context) +bool QScxmlCppDataModel::setScxmlProperty(const QString &name, const QVariant &value, + const QString &context) { Q_UNUSED(name); Q_UNUSED(value); Q_UNUSED(context); - Q_UNREACHABLE(); return false; } /*! - * Returns \c true if the state machine is in the state specified by \a stateName, \c false - * otherwise. + Returns \c true if the state machine is in the state specified by + \a stateName, \c false otherwise. */ bool QScxmlCppDataModel::inState(const QString &stateName) const { diff --git a/src/scxml/qscxmlcppdatamodel.h b/src/scxml/qscxmlcppdatamodel.h index fb59336..244259f 100644 --- a/src/scxml/qscxmlcppdatamodel.h +++ b/src/scxml/qscxmlcppdatamodel.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef CPPDATAMODEL_H -#define CPPDATAMODEL_H +#ifndef QSCXMLCPPDATAMODEL_H +#define QSCXMLCPPDATAMODEL_H #include <QtScxml/qscxmldatamodel.h> @@ -59,15 +59,12 @@ class Q_SCXML_EXPORT QScxmlCppDataModel: public QScxmlDataModel Q_DECLARE_PRIVATE(QScxmlCppDataModel) public: explicit QScxmlCppDataModel(QObject *parent = nullptr); - ~QScxmlCppDataModel(); Q_INVOKABLE bool setup(const QVariantMap &initialDataValues) Q_DECL_OVERRIDE; -#ifndef Q_QDOC - void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; - void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; - bool evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) Q_DECL_OVERRIDE Q_DECL_FINAL; -#endif // Q_QDOC + void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE; + void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE; + void evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) Q_DECL_OVERRIDE; void setScxmlEvent(const QScxmlEvent &scxmlEvent) Q_DECL_OVERRIDE Q_DECL_FINAL; const QScxmlEvent &scxmlEvent() const; @@ -81,4 +78,4 @@ public: QT_END_NAMESPACE -#endif // CPPDATAMODEL_H +#endif // QSCXMLCPPDATAMODEL_H diff --git a/src/scxml/qscxmlcppdatamodel_p.h b/src/scxml/qscxmlcppdatamodel_p.h index 1712a73..b342e39 100644 --- a/src/scxml/qscxmlcppdatamodel_p.h +++ b/src/scxml/qscxmlcppdatamodel_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef CPPDATAMODEL_P_H -#define CPPDATAMODEL_P_H +#ifndef QSCXMLCPPDATAMODEL_P_H +#define QSCXMLCPPDATAMODEL_P_H // // W A R N I N G @@ -65,4 +65,4 @@ public: QT_END_NAMESPACE -#endif // CPPDATAMODEL_P_H +#endif // QSCXMLCPPDATAMODEL_P_H diff --git a/src/scxml/qscxmldatamodel.cpp b/src/scxml/qscxmldatamodel.cpp index 9762e92..f7a7bb2 100644 --- a/src/scxml/qscxmldatamodel.cpp +++ b/src/scxml/qscxmldatamodel.cpp @@ -45,14 +45,27 @@ QT_BEGIN_NAMESPACE /*! - * \class QScxmlDataModel::ForeachLoopBody - * \internal + \class QScxmlDataModel::ForeachLoopBody + \brief The ForeachLoopBody class represents a function to be executed on + each iteration of an SCXML foreach loop. + \since 5.8 + \inmodule QtScxml */ +/*! + Destroys a foreach loop body. + */ QScxmlDataModel::ForeachLoopBody::~ForeachLoopBody() {} /*! + \fn QScxmlDataModel::ForeachLoopBody::run(bool *ok) + + This function is executed on each iteration. If the execution fails, \a ok is + set to \c false, otherwise it is set to \c true. + */ + +/*! * \class QScxmlDataModel * \brief The QScxmlDataModel class is the data model base class for a Qt SCXML * state machine. @@ -89,18 +102,14 @@ QScxmlDataModel::QScxmlDataModel(QObject *parent) } /*! - * \internal + Creates a new data model from the private object \a dd, with the parent + object \a parent. */ QScxmlDataModel::QScxmlDataModel(QScxmlDataModelPrivate &dd, QObject *parent) : QObject(dd, parent) { } -/*! \internal */ -QScxmlDataModel::~QScxmlDataModel() -{ -} - /*! * Sets the state machine this model belongs to to \a stateMachine. There is a * 1:1 relation between state machines and models. After setting the state @@ -128,11 +137,6 @@ QScxmlStateMachine *QScxmlDataModel::stateMachine() const return d->m_stateMachine; } -QScxmlTableData *QScxmlDataModel::tableData() const -{ - return stateMachine()->tableData(); -} - QScxmlDataModel *QScxmlDataModelPrivate::instantiateDataModel(DocumentModel::Scxml::DataModelType type) { QScxmlDataModel *dataModel = Q_NULLPTR; @@ -194,4 +198,59 @@ QScxmlDataModel *QScxmlDataModelPrivate::instantiateDataModel(DocumentModel::Scx * Returns \c true if successful or \c false if an error occurred. */ +/*! + * \fn QScxmlDataModel::evaluateToString( + * QScxmlExecutableContent::EvaluatorId id, bool *ok) + * Evaluates the executable content pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. + * Returns the result of the evaluation as a QString. + */ + +/*! + * \fn QScxmlDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, + * bool *ok) + * Evaluates the executable content pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. + * Returns the result of the evaluation as a boolean value. + */ + +/*! + * \fn QScxmlDataModel::evaluateToVariant( + * QScxmlExecutableContent::EvaluatorId id, bool *ok) + * Evaluates the executable content pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. + * Returns the result of the evaluation as a QVariant. + */ + +/*! + * \fn QScxmlDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, + * bool *ok) + * Evaluates the executable content pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. + * The execution is expected to return no result. + */ + +/*! + * \fn QScxmlDataModel::evaluateAssignment( + * QScxmlExecutableContent::EvaluatorId id, bool *ok) + * Evaluates the assignment pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. + */ + +/*! + * \fn QScxmlDataModel::evaluateInitialization( + * QScxmlExecutableContent::EvaluatorId id, bool *ok) + * Evaluates the initialization pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. + */ + +/*! + * \fn QScxmlDataModel::evaluateForeach( + * QScxmlExecutableContent::EvaluatorId id, bool *ok, + * ForeachLoopBody *body) + * Evaluates the foreach loop pointed to by \a id and sets \a ok to + * \c false if there was an error or to \c true if there was not. The + * \a body is executed on each iteration. + */ + QT_END_NAMESPACE diff --git a/src/scxml/qscxmldatamodel.h b/src/scxml/qscxmldatamodel.h index 845d8bf..d34a84a 100644 --- a/src/scxml/qscxmldatamodel.h +++ b/src/scxml/qscxmldatamodel.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef DATAMODEL_H -#define DATAMODEL_H +#ifndef QSCXMLDATAMODEL_H +#define QSCXMLDATAMODEL_H #include <QtScxml/qscxmlexecutablecontent.h> @@ -64,27 +64,24 @@ public: { public: virtual ~ForeachLoopBody(); - virtual bool run() = 0; + virtual void run(bool *ok) = 0; }; public: explicit QScxmlDataModel(QObject *parent = nullptr); - ~QScxmlDataModel(); void setStateMachine(QScxmlStateMachine *stateMachine); QScxmlStateMachine *stateMachine() const; Q_INVOKABLE virtual bool setup(const QVariantMap &initialDataValues) = 0; -#ifndef Q_QDOC virtual QString evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0; virtual bool evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0; virtual QVariant evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0; virtual void evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0; virtual void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0; virtual void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0; - virtual bool evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) = 0; -#endif // Q_QDOC + virtual void evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) = 0; virtual void setScxmlEvent(const QScxmlEvent &event) = 0; @@ -97,11 +94,8 @@ Q_SIGNALS: protected: explicit QScxmlDataModel(QScxmlDataModelPrivate &dd, QObject *parent = nullptr); -#ifndef Q_QDOC - QScxmlTableData *tableData() const; -#endif // Q_QDOC }; QT_END_NAMESPACE -#endif // DATAMODEL_H +#endif // QSCXMLDATAMODEL_H diff --git a/src/scxml/qscxmlecmascriptdatamodel.cpp b/src/scxml/qscxmlecmascriptdatamodel.cpp index 32c49f0..e517989 100644 --- a/src/scxml/qscxmlecmascriptdatamodel.cpp +++ b/src/scxml/qscxmlecmascriptdatamodel.cpp @@ -121,25 +121,26 @@ public: QJSEngine *engine = assertEngine(); dataModel = engine->globalObject(); - qCDebug(qscxmlLog) << stateMachine() << "initializing the datamodel"; + qCDebug(qscxmlLog) << m_stateMachine << "initializing the datamodel"; setupSystemVariables(); } void setupSystemVariables() { setReadonlyProperty(&dataModel, QStringLiteral("_sessionid"), - stateMachine()->sessionId()); + m_stateMachine->sessionId()); - setReadonlyProperty(&dataModel, QStringLiteral("_name"), stateMachine()->name()); + setReadonlyProperty(&dataModel, QStringLiteral("_name"), m_stateMachine->name()); QJSEngine *engine = assertEngine(); auto scxml = engine->newObject(); - scxml.setProperty(QStringLiteral("location"), QStringLiteral("#_scxml_%1").arg(stateMachine()->sessionId())); + scxml.setProperty(QStringLiteral("location"), QStringLiteral("#_scxml_%1") + .arg(m_stateMachine->sessionId())); auto ioProcs = engine->newObject(); setReadonlyProperty(&ioProcs, QStringLiteral("scxml"), scxml); setReadonlyProperty(&dataModel, QStringLiteral("_ioprocessors"), ioProcs); - auto platformVars = QScxmlPlatformProperties::create(engine, stateMachine()); + auto platformVars = QScxmlPlatformProperties::create(engine, m_stateMachine); dataModel.setProperty(QStringLiteral("_x"), platformVars->jsValue()); dataModel.setProperty(QStringLiteral("In"), engine->evaluate( @@ -204,12 +205,6 @@ public: return engine->toScriptValue(data); } - QScxmlStateMachine *stateMachine() const - { - Q_Q(const QScxmlEcmaScriptDataModel); - return q->stateMachine(); - } - QJSEngine *assertEngine() { if (!jsEngine) { @@ -230,8 +225,7 @@ public: QString string(StringId id) const { - Q_Q(const QScxmlEcmaScriptDataModel); - return q->tableData()->string(id); + return m_stateMachine->tableData()->string(id); } bool hasProperty(const QString &name) const @@ -265,7 +259,7 @@ public: void submitError(const QString &type, const QString &msg, const QString &sendid = QString()) { - QScxmlStateMachinePrivate::get(stateMachine())->submitError(type, msg, sendid); + QScxmlStateMachinePrivate::get(m_stateMachine)->submitError(type, msg, sendid); } public: @@ -370,11 +364,6 @@ QScxmlEcmaScriptDataModel::QScxmlEcmaScriptDataModel(QObject *parent) : QScxmlDataModel(*(new QScxmlEcmaScriptDataModelPrivate), parent) {} -/*! \internal */ -QScxmlEcmaScriptDataModel::~QScxmlEcmaScriptDataModel() -{ -} - /*! \reimp */ @@ -386,7 +375,7 @@ bool QScxmlEcmaScriptDataModel::setup(const QVariantMap &initialDataValues) bool ok = true; QJSValue undefined(QJSValue::UndefinedValue); // See B.2.1, and test456. int count; - StringId *names = tableData()->dataNames(&count); + StringId *names = d->m_stateMachine->tableData()->dataNames(&count); for (int i = 0; i < count; ++i) { auto name = d->string(names[i]); QJSValue v = undefined; @@ -404,44 +393,64 @@ bool QScxmlEcmaScriptDataModel::setup(const QVariantMap &initialDataValues) return ok; } -QString QScxmlEcmaScriptDataModel::evaluateToString(EvaluatorId id, bool *ok) +/*! + \reimp + */ +QString QScxmlEcmaScriptDataModel::evaluateToString(QScxmlExecutableContent::EvaluatorId id, + bool *ok) { Q_D(QScxmlEcmaScriptDataModel); - const EvaluatorInfo &info = tableData()->evaluatorInfo(id); + const EvaluatorInfo &info = d->m_stateMachine->tableData()->evaluatorInfo(id); return d->evalStr(d->string(info.expr), d->string(info.context), ok); } -bool QScxmlEcmaScriptDataModel::evaluateToBool(EvaluatorId id, bool *ok) +/*! + \reimp + */ +bool QScxmlEcmaScriptDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, + bool *ok) { Q_D(QScxmlEcmaScriptDataModel); - const EvaluatorInfo &info = tableData()->evaluatorInfo(id); + const EvaluatorInfo &info = d->m_stateMachine->tableData()->evaluatorInfo(id); return d->evalBool(d->string(info.expr), d->string(info.context), ok); } -QVariant QScxmlEcmaScriptDataModel::evaluateToVariant(EvaluatorId id, bool *ok) +/*! + \reimp + */ +QVariant QScxmlEcmaScriptDataModel::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, + bool *ok) { Q_D(QScxmlEcmaScriptDataModel); - const EvaluatorInfo &info = tableData()->evaluatorInfo(id); + const EvaluatorInfo &info = d->m_stateMachine->tableData()->evaluatorInfo(id); return d->evalJSValue(d->string(info.expr), d->string(info.context), ok).toVariant(); } -void QScxmlEcmaScriptDataModel::evaluateToVoid(EvaluatorId id, bool *ok) +/*! + \reimp + */ +void QScxmlEcmaScriptDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, + bool *ok) { Q_D(QScxmlEcmaScriptDataModel); - const EvaluatorInfo &info = tableData()->evaluatorInfo(id); + const EvaluatorInfo &info = d->m_stateMachine->tableData()->evaluatorInfo(id); d->eval(d->string(info.expr), d->string(info.context), ok); } -void QScxmlEcmaScriptDataModel::evaluateAssignment(EvaluatorId id, bool *ok) +/*! + \reimp + */ +void QScxmlEcmaScriptDataModel::evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, + bool *ok) { Q_D(QScxmlEcmaScriptDataModel); Q_ASSERT(ok); - const AssignmentInfo &info = tableData()->assignmentInfo(id); + const AssignmentInfo &info = d->m_stateMachine->tableData()->assignmentInfo(id); QString dest = d->string(info.dest); @@ -456,10 +465,14 @@ void QScxmlEcmaScriptDataModel::evaluateAssignment(EvaluatorId id, bool *ok) } } -void QScxmlEcmaScriptDataModel::evaluateInitialization(EvaluatorId id, bool *ok) +/*! + \reimp + */ +void QScxmlEcmaScriptDataModel::evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, + bool *ok) { Q_D(QScxmlEcmaScriptDataModel); - const AssignmentInfo &info = tableData()->assignmentInfo(id); + const AssignmentInfo &info = d->m_stateMachine->tableData()->assignmentInfo(id); QString dest = d->string(info.dest); if (d->initialDataNames.contains(dest)) { *ok = true; // silently ignore the <data> tag @@ -469,18 +482,22 @@ void QScxmlEcmaScriptDataModel::evaluateInitialization(EvaluatorId id, bool *ok) evaluateAssignment(id, ok); } -bool QScxmlEcmaScriptDataModel::evaluateForeach(EvaluatorId id, bool *ok, ForeachLoopBody *body) +/*! + \reimp + */ +void QScxmlEcmaScriptDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, + ForeachLoopBody *body) { Q_D(QScxmlEcmaScriptDataModel); Q_ASSERT(ok); Q_ASSERT(body); - const ForeachInfo &info = tableData()->foreachInfo(id); + const ForeachInfo &info = d->m_stateMachine->tableData()->foreachInfo(id); QJSValue jsArray = d->property(d->string(info.array)); if (!jsArray.isArray()) { d->submitError(QStringLiteral("error.execution"), QStringLiteral("invalid array '%1' in %2").arg(d->string(info.array), d->string(info.context))); *ok = false; - return false; + return; } QString item = d->string(info.item); @@ -490,7 +507,7 @@ bool QScxmlEcmaScriptDataModel::evaluateForeach(EvaluatorId id, bool *ok, Foreac d->submitError(QStringLiteral("error.execution"), QStringLiteral("invalid item '%1' in %2") .arg(d->string(info.item), d->string(info.context))); *ok = false; - return false; + return; } const int length = jsArray.property(QStringLiteral("length")).toInt(); @@ -502,17 +519,17 @@ bool QScxmlEcmaScriptDataModel::evaluateForeach(EvaluatorId id, bool *ok, Foreac QJSValue currentItem = jsArray.property(static_cast<quint32>(currentIndex)); *ok = d->setProperty(item, currentItem, context); if (!*ok) - return false; + return; if (hasIndex) { *ok = d->setProperty(idx, currentIndex, context); if (!*ok) - return false; + return; } - if (!body->run()) - return false; + body->run(ok); + if (!*ok) + return; } - - return true; + *ok = true; } /*! diff --git a/src/scxml/qscxmlecmascriptdatamodel.h b/src/scxml/qscxmlecmascriptdatamodel.h index b1d9f55..7c22044 100644 --- a/src/scxml/qscxmlecmascriptdatamodel.h +++ b/src/scxml/qscxmlecmascriptdatamodel.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef ECMASCRIPTDATAMODEL_H -#define ECMASCRIPTDATAMODEL_H +#ifndef QSCXMLECMASCRIPTDATAMODEL_H +#define QSCXMLECMASCRIPTDATAMODEL_H #include <QtScxml/qscxmldatamodel.h> @@ -52,19 +52,16 @@ class Q_SCXML_EXPORT QScxmlEcmaScriptDataModel: public QScxmlDataModel Q_DECLARE_PRIVATE(QScxmlEcmaScriptDataModel) public: explicit QScxmlEcmaScriptDataModel(QObject *parent = nullptr); - ~QScxmlEcmaScriptDataModel(); Q_INVOKABLE bool setup(const QVariantMap &initialDataValues) Q_DECL_OVERRIDE; -#ifndef Q_QDOC QString evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; bool evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; QVariant evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; void evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; - bool evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) Q_DECL_OVERRIDE Q_DECL_FINAL; -#endif // Q_QDOC + void evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) Q_DECL_OVERRIDE Q_DECL_FINAL; void setScxmlEvent(const QScxmlEvent &event) Q_DECL_OVERRIDE; @@ -75,4 +72,4 @@ public: QT_END_NAMESPACE -#endif // ECMASCRIPTDATAMODEL_H +#endif // QSCXMLECMASCRIPTDATAMODEL_H diff --git a/src/scxml/qscxmlecmascriptplatformproperties_p.h b/src/scxml/qscxmlecmascriptplatformproperties_p.h index fef53ea..cf1c4a9 100644 --- a/src/scxml/qscxmlecmascriptplatformproperties_p.h +++ b/src/scxml/qscxmlecmascriptplatformproperties_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef ECMASCRIPTPLATFORMPROPERTIES_P_H -#define ECMASCRIPTPLATFORMPROPERTIES_P_H +#ifndef QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H +#define QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H // // W A R N I N G @@ -86,4 +86,4 @@ private: QT_END_NAMESPACE -#endif // ECMASCRIPTPLATFORMPROPERTIES_P_H +#endif // QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H diff --git a/src/scxml/qscxmlevent.h b/src/scxml/qscxmlevent.h index c4f517f..267f5ec 100644 --- a/src/scxml/qscxmlevent.h +++ b/src/scxml/qscxmlevent.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SCXMLEVENT_H -#define SCXMLEVENT_H +#ifndef QSCXMLEVENT_H +#define QSCXMLEVENT_H #include <QtScxml/qscxmlglobals.h> @@ -119,4 +119,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QScxmlEvent) -#endif // SCXMLEVENT_H +#endif // QSCXMLEVENT_H diff --git a/src/scxml/qscxmlevent_p.h b/src/scxml/qscxmlevent_p.h index aef14d1..15bd6fa 100644 --- a/src/scxml/qscxmlevent_p.h +++ b/src/scxml/qscxmlevent_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SCXMLEVENT_P_H -#define SCXMLEVENT_P_H +#ifndef QSCXMLEVENT_P_H +#define QSCXMLEVENT_P_H // // W A R N I N G @@ -177,5 +177,5 @@ public: QT_END_NAMESPACE -#endif // SCXMLEVENT_P_H +#endif // QSCXMLEVENT_P_H diff --git a/src/scxml/qscxmlexecutablecontent.cpp b/src/scxml/qscxmlexecutablecontent.cpp index 5df8b1f..682ea47 100644 --- a/src/scxml/qscxmlexecutablecontent.cpp +++ b/src/scxml/qscxmlexecutablecontent.cpp @@ -46,6 +46,177 @@ QT_BEGIN_NAMESPACE using namespace QScxmlExecutableContent; +/*! + \namespace QScxmlExecutableContent + \inmodule QtScxml + \since 5.8 + \brief The QScxmlExecutableContent namespace contains various types used + to interpret executable content in state machines. + */ + +/*! + \typedef QScxmlExecutableContent::ContainerId + \inmodule QtScxml + \since 5.8 + \brief ID for a container holding executable content. + */ + +/*! + \typedef QScxmlExecutableContent::EvaluatorId + \inmodule QtScxml + \since 5.8 + \brief ID for a unit of executable content. + */ + +/*! + \typedef QScxmlExecutableContent::InstructionId + \inmodule QtScxml + \since 5.8 + \brief ID for an instruction of executable content. + */ + +/*! + \typedef QScxmlExecutableContent::StringId + \inmodule QtScxml + \since 5.8 + \brief ID for a string contained in executable content. + */ + +/*! + \class QScxmlExecutableContent::EvaluatorInfo + \brief The EvaluatorInfo class represents a unit of executable content. + \since 5.8 + \inmodule QtScxml + */ + +/*! + \variable QScxmlExecutableContent::EvaluatorInfo::expr + \brief The expression to be evaluated + */ + +/*! + \variable QScxmlExecutableContent::EvaluatorInfo::context + \brief The context for evaluating the expression + */ + +/*! + \class QScxmlExecutableContent::AssignmentInfo + \brief The AssingmentInfo class represents a data assignment. + \since 5.8 + \inmodule QtScxml + */ + +/*! + \variable QScxmlExecutableContent::AssignmentInfo::expr + \brief The expression to be evaluated + */ + +/*! + \variable QScxmlExecutableContent::AssignmentInfo::context + \brief The context for evaluating the expression + */ + +/*! + \variable QScxmlExecutableContent::AssignmentInfo::dest + \brief The name of the data item to assign to + */ + +/*! + \class QScxmlExecutableContent::ForeachInfo + \brief The ForeachInfo class represents a foreach construct. + \since 5.8 + \inmodule QtScxml + */ + +/*! + \variable QScxmlExecutableContent::ForeachInfo::array + \brief The name of the array that is iterated over + */ + +/*! + \variable QScxmlExecutableContent::ForeachInfo::item + \brief The name of the iteration variable + */ + +/*! + \variable QScxmlExecutableContent::ForeachInfo::index + \brief The name of the index variable + */ + +/*! + \variable QScxmlExecutableContent::ForeachInfo::context + \brief The context for evaluating the expression + */ + +/*! + \class QScxmlExecutableContent::ParameterInfo + \brief The ParameterInfo class represents a parameter to a service + invocation. + \since 5.8 + \inmodule QtScxml + */ + +/*! + \variable QScxmlExecutableContent::ParameterInfo::name + \brief The name of the parameter + */ + +/*! + \variable QScxmlExecutableContent::ParameterInfo::expr + \brief The expression to be evaluated + */ + +/*! + \variable QScxmlExecutableContent::ParameterInfo::location + \brief The data model name of the item to be passed as a parameter + */ + +/*! + \class QScxmlExecutableContent::InvokeInfo + \brief The InvokeInfo class represents a service invocation. + \since 5.8 + \inmodule QtScxml + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::id + \brief The ID specified by the \c id attribute in the \c <invoke> element. + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::prefix + \brief The unique prefix for this invocation in the context of the state + from which it is called + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::location + \brief The data model location to write the invocation ID to + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::context + \brief The context to interpret the location in + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::expr + \brief The expression representing the srcexpr of the invoke element + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::finalize + \brief The ID of the container of executable content to be run on finalizing + the invocation + */ + +/*! + \variable QScxmlExecutableContent::InvokeInfo::autoforward + \brief Whether events should automatically be forwarded to the invoked + service + */ + + #ifndef BUILD_QSCXMLC static int parseTime(const QString &t, bool *ok = 0) { @@ -216,11 +387,9 @@ const InstructionId *QScxmlExecutionEngine::step(const InstructionId *ip, bool * , loopStart(loopStart) {} - bool run() Q_DECL_OVERRIDE + void run(bool *ok) Q_DECL_OVERRIDE { - bool ok = true; - engine->step(loopStart, &ok); - return ok; + engine->step(loopStart, ok); } }; @@ -229,7 +398,7 @@ const InstructionId *QScxmlExecutionEngine::step(const InstructionId *ip, bool * const InstructionId *loopStart = _foreach->blockstart(); ip += _foreach->size(); LoopBody body(this, loopStart); - *ok = dataModel->evaluateForeach(_foreach->doIt, ok, &body) && *ok; + dataModel->evaluateForeach(_foreach->doIt, ok, &body); return ip; } diff --git a/src/scxml/qscxmlexecutablecontent.h b/src/scxml/qscxmlexecutablecontent.h index cdf6b2b..8d2ef13 100644 --- a/src/scxml/qscxmlexecutablecontent.h +++ b/src/scxml/qscxmlexecutablecontent.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef EXECUTABLECONTENT_H -#define EXECUTABLECONTENT_H +#ifndef QSCXMLEXECUTABLECONTENT_H +#define QSCXMLEXECUTABLECONTENT_H #include <QtScxml/qscxmlglobals.h> @@ -99,4 +99,4 @@ struct InvokeInfo { QT_END_NAMESPACE -#endif // EXECUTABLECONTENT_H +#endif // QSCXMLEXECUTABLECONTENT_H diff --git a/src/scxml/qscxmlexecutablecontent_p.h b/src/scxml/qscxmlexecutablecontent_p.h index 4429686..cfaec55 100644 --- a/src/scxml/qscxmlexecutablecontent_p.h +++ b/src/scxml/qscxmlexecutablecontent_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef EXECUTABLECONTENT_P_H -#define EXECUTABLECONTENT_P_H +#ifndef QSCXMLEXECUTABLECONTENT_P_H +#define QSCXMLEXECUTABLECONTENT_P_H // // W A R N I N G @@ -61,6 +61,7 @@ #include <QtScxml/qscxmlstatemachine.h> #endif // BUILD_QSCXMLC +#ifndef Q_QDOC QT_BEGIN_NAMESPACE namespace QScxmlExecutableContent { @@ -509,5 +510,6 @@ private: }; QT_END_NAMESPACE +#endif // Q_QDOC -#endif // EXECUTABLECONTENT_P_H +#endif // QSCXMLEXECUTABLECONTENT_P_H diff --git a/src/scxml/qscxmlglobals.h b/src/scxml/qscxmlglobals.h index d9bf7b5..8672aa1 100644 --- a/src/scxml/qscxmlglobals.h +++ b/src/scxml/qscxmlglobals.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SCXMLGLOBALS_H -#define SCXMLGLOBALS_H +#ifndef QSCXMLGLOBALS_H +#define QSCXMLGLOBALS_H #include <QtCore/qglobal.h> QT_BEGIN_NAMESPACE @@ -55,5 +55,5 @@ QT_BEGIN_NAMESPACE QT_END_NAMESPACE -#endif // SCXMLGLOBALS_H +#endif // QSCXMLGLOBALS_H diff --git a/src/scxml/qscxmlglobals_p.h b/src/scxml/qscxmlglobals_p.h index 48b3d73..559636b 100644 --- a/src/scxml/qscxmlglobals_p.h +++ b/src/scxml/qscxmlglobals_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SCXMLGLOBALS_P_H -#define SCXMLGLOBALS_P_H +#ifndef QSCXMLGLOBALS_P_H +#define QSCXMLGLOBALS_P_H // // W A R N I N G @@ -62,4 +62,4 @@ Q_DECLARE_LOGGING_CATEGORY(scxmlLog) QT_END_NAMESPACE -#endif // SCXMLGLOBALS_P_H +#endif // QSCXMLGLOBALS_P_H diff --git a/src/scxml/qscxmlinvokableservice.cpp b/src/scxml/qscxmlinvokableservice.cpp index 9af6be7..a897eaa 100644 --- a/src/scxml/qscxmlinvokableservice.cpp +++ b/src/scxml/qscxmlinvokableservice.cpp @@ -43,6 +43,112 @@ 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) { @@ -59,9 +165,18 @@ QScxmlInvokableServiceFactoryPrivate::QScxmlInvokableServiceFactoryPrivate( , 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, - QObject *parent) : - QObject(*(new QScxmlInvokableServicePrivate(parentStateMachine)), parent) + QScxmlInvokableServiceFactory *factory) : + QObject(*(new QScxmlInvokableServicePrivate(parentStateMachine)), factory) { } @@ -71,58 +186,43 @@ QScxmlStateMachine *QScxmlInvokableService::parentStateMachine() const return d->parentStateMachine; } -void QScxmlInvokableService::finalize(QScxmlExecutableContent::ContainerId finalize) -{ - if (finalize != QScxmlExecutableContent::NoInstruction) { - auto psm = parentStateMachine(); - qCDebug(qscxmlLog) << psm << "running finalize on event"; - auto smp = QScxmlStateMachinePrivate::get(psm); - smp->m_executionEngine->execute(finalize); - } -} - -QScxmlInvokableService::QScxmlInvokableService(QScxmlInvokableServicePrivate &dd, QObject *parent) : - QObject(dd, parent) -{ -} - QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory( const QScxmlExecutableContent::InvokeInfo &invokeInfo, const QVector<QScxmlExecutableContent::StringId> &names, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters) - : d(new QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters)) + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, + QObject *parent) + : QObject(*(new QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters)), parent) {} -QScxmlInvokableServiceFactory::~QScxmlInvokableServiceFactory() -{ - delete d; -} - -QScxmlExecutableContent::InvokeInfo QScxmlInvokableServiceFactory::invokeInfo() const +const QScxmlExecutableContent::InvokeInfo &QScxmlInvokableServiceFactory::invokeInfo() const { + Q_D(const QScxmlInvokableServiceFactory); return d->invokeInfo; } -QVector<QScxmlExecutableContent::ParameterInfo> QScxmlInvokableServiceFactory::parameters() const +const QVector<QScxmlExecutableContent::ParameterInfo> & +QScxmlInvokableServiceFactory::parameters() const { + Q_D(const QScxmlInvokableServiceFactory); return d->parameters; } -QVector<QScxmlExecutableContent::StringId> QScxmlInvokableServiceFactory::names() const +const QVector<QScxmlExecutableContent::StringId> &QScxmlInvokableServiceFactory::names() const { + Q_D(const QScxmlInvokableServiceFactory); return d->names; } -QString QScxmlInvokableServiceFactoryPrivate::calculateSrcexpr(QScxmlStateMachine *parent, - bool *ok) const +QString calculateSrcexpr(QScxmlStateMachine *parent, QScxmlExecutableContent::EvaluatorId srcexpr, + bool *ok) { Q_ASSERT(ok); *ok = true; auto dataModel = parent->dataModel(); - if (invokeInfo.expr != QScxmlExecutableContent::NoEvaluator) { + if (srcexpr != QScxmlExecutableContent::NoEvaluator) { *ok = false; - auto v = dataModel->evaluateToString(invokeInfo.expr, ok); + auto v = dataModel->evaluateToString(srcexpr, ok); if (!*ok) return QString(); return v; @@ -217,106 +317,142 @@ QVariantMap QScxmlInvokableServicePrivate::calculateData( return result; } -QScxmlScxmlServicePrivate::QScxmlScxmlServicePrivate(QScxmlStateMachine *stateMachine, - QScxmlStateMachine *parentStateMachine) : - QScxmlInvokableServicePrivate(parentStateMachine), stateMachine(stateMachine) -{} - -QScxmlScxmlServicePrivate::~QScxmlScxmlServicePrivate() +QScxmlScxmlService::~QScxmlScxmlService() { delete stateMachine; } +/*! + Creates a SCXML service wrapping \a stateMachine, invoked from + \a parentStateMachine, as a child of \a factory. + */ QScxmlScxmlService::QScxmlScxmlService(QScxmlStateMachine *stateMachine, QScxmlStateMachine *parentStateMachine, - QObject *parent) - : QScxmlInvokableService(*(new QScxmlScxmlServicePrivate(stateMachine, parentStateMachine)), - parent) + QScxmlInvokableServiceFactory *factory) + : QScxmlInvokableService(parentStateMachine, factory), stateMachine(stateMachine) { QScxmlStateMachinePrivate::get(stateMachine)->m_parentStateMachine = parentStateMachine; } -bool QScxmlScxmlService::start(const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, - const QVector<QScxmlExecutableContent::StringId> &names) +/*! + * \reimp + */ +bool QScxmlScxmlService::start() { - Q_D(QScxmlScxmlService); - qCDebug(qscxmlLog) << parentStateMachine() << "preparing to start" << d->stateMachine; + Q_D(QScxmlInvokableService); + qCDebug(qscxmlLog) << parentStateMachine() << "preparing to start" << stateMachine; + + const QScxmlInvokableServiceFactory *factory + = qobject_cast<QScxmlInvokableServiceFactory *>(parent()); + Q_ASSERT(factory); bool ok = false; - auto id = d->calculateId(parentStateMachine(), invokeInfo, &ok); + auto id = d->calculateId(parentStateMachine(), factory->invokeInfo(), &ok); if (!ok) return false; - auto data = d->calculateData(parentStateMachine(), parameters, names, &ok); + auto data = d->calculateData(parentStateMachine(), factory->parameters(), factory->names(), + &ok); if (!ok) return false; - QScxmlStateMachinePrivate::get(d->stateMachine)->m_sessionId = id; - d->stateMachine->setInitialValues(data); - if (d->stateMachine->init()) { - qCDebug(qscxmlLog) << parentStateMachine() << "starting" << d->stateMachine; - d->stateMachine->start(); + QScxmlStateMachinePrivate::get(stateMachine)->m_sessionId = id; + stateMachine->setInitialValues(data); + if (stateMachine->init()) { + qCDebug(qscxmlLog) << parentStateMachine() << "starting" << stateMachine; + stateMachine->start(); return true; } - qCDebug(qscxmlLog) << parentStateMachine() << "failed to start" << d->stateMachine; + qCDebug(qscxmlLog) << parentStateMachine() << "failed to start" << stateMachine; return false; } +/*! + \reimp + */ QString QScxmlScxmlService::id() const { - Q_D(const QScxmlScxmlService); - return d->stateMachine->sessionId(); + return stateMachine->sessionId(); } +/*! + \reimp + */ QString QScxmlScxmlService::name() const { - Q_D(const QScxmlScxmlService); - return d->stateMachine->name(); + return stateMachine->name(); } +/*! + \reimp + */ void QScxmlScxmlService::postEvent(QScxmlEvent *event) { - Q_D(QScxmlScxmlService); - QScxmlStateMachinePrivate::get(d->stateMachine)->postEvent(event); -} - -QScxmlStateMachine *QScxmlScxmlService::stateMachine() const -{ - Q_D(const QScxmlScxmlService); - return d->stateMachine; -} - -QScxmlScxmlServiceFactory::QScxmlScxmlServiceFactory( - const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::StringId> &names, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters) - : QScxmlInvokableServiceFactory(invokeInfo, names, parameters) -{} - -QScxmlScxmlService *QScxmlScxmlServiceFactory::invokeStatic(QScxmlStateMachine *childStateMachine, - QScxmlStateMachine *parentStateMachine) -{ - QScxmlStateMachinePrivate::get(childStateMachine)->setIsInvoked(true); - return new QScxmlScxmlService(childStateMachine, parentStateMachine); + QScxmlStateMachinePrivate::get(stateMachine)->postEvent(event); } +/*! + 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) - : QScxmlScxmlServiceFactory(invokeInfo, names, parameters) + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, + QObject *parent) + : QScxmlInvokableServiceFactory(invokeInfo, names, parameters, parent) {} +/*! + \reimp + */ QScxmlInvokableService *QScxmlDynamicScxmlServiceFactory::invoke( QScxmlStateMachine *parentStateMachine) { bool ok = true; - auto srcexpr = d->calculateSrcexpr(parentStateMachine, &ok); + auto srcexpr = calculateSrcexpr(parentStateMachine, invokeInfo().expr, &ok); if (!ok) return Q_NULLPTR; - return invokeDynamic(parentStateMachine, srcexpr); + 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 diff --git a/src/scxml/qscxmlinvokableservice.h b/src/scxml/qscxmlinvokableservice.h index 36ebcd0..a3c09e8 100644 --- a/src/scxml/qscxmlinvokableservice.h +++ b/src/scxml/qscxmlinvokableservice.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE class QScxmlEvent; class QScxmlStateMachine; +class QScxmlInvokableServiceFactory; class QScxmlInvokableServicePrivate; class Q_SCXML_EXPORT QScxmlInvokableService : public QObject { @@ -59,101 +60,66 @@ class Q_SCXML_EXPORT QScxmlInvokableService : public QObject public: QScxmlInvokableService(QScxmlStateMachine *parentStateMachine, - QObject *parent = nullptr); + QScxmlInvokableServiceFactory *parent); QScxmlStateMachine *parentStateMachine() const; - virtual bool start(const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, - const QVector<QScxmlExecutableContent::StringId> &names) = 0; + virtual bool start() = 0; virtual QString id() const = 0; virtual QString name() const = 0; virtual void postEvent(QScxmlEvent *event) = 0; - - void finalize(QScxmlExecutableContent::ContainerId finalize); - -protected: - QScxmlInvokableService(QScxmlInvokableServicePrivate &dd, QObject *parent = nullptr); }; class QScxmlInvokableServiceFactoryPrivate; -class Q_SCXML_EXPORT QScxmlInvokableServiceFactory +class Q_SCXML_EXPORT QScxmlInvokableServiceFactory : public QObject { + Q_OBJECT + Q_DECLARE_PRIVATE(QScxmlInvokableServiceFactory) + Q_PROPERTY(QScxmlExecutableContent::InvokeInfo invokeInfo READ invokeInfo CONSTANT) + Q_PROPERTY(QVector<QScxmlExecutableContent::ParameterInfo> parameters READ parameters CONSTANT) + Q_PROPERTY(QVector<QScxmlExecutableContent::StringId> names READ names CONSTANT) + public: QScxmlInvokableServiceFactory( const QScxmlExecutableContent::InvokeInfo &invokeInfo, const QVector<QScxmlExecutableContent::StringId> &names, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters); - virtual ~QScxmlInvokableServiceFactory(); + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, + QObject *parent = nullptr); virtual QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) = 0; - QScxmlExecutableContent::InvokeInfo invokeInfo() const; - QVector<QScxmlExecutableContent::ParameterInfo> parameters() const; - QVector<QScxmlExecutableContent::StringId> names() const; + const QScxmlExecutableContent::InvokeInfo &invokeInfo() const; + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters() const; + const QVector<QScxmlExecutableContent::StringId> &names() const; protected: - QScxmlInvokableServiceFactoryPrivate *d; + QScxmlInvokableServiceFactory(QScxmlInvokableServiceFactoryPrivate &dd, QObject *parent); }; -class QScxmlScxmlServicePrivate; -class Q_SCXML_EXPORT QScxmlScxmlService: public QScxmlInvokableService +class QScxmlStaticScxmlServiceFactoryPrivate; +class Q_SCXML_EXPORT QScxmlStaticScxmlServiceFactory: public QScxmlInvokableServiceFactory { Q_OBJECT - Q_DECLARE_PRIVATE(QScxmlScxmlService) - Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine CONSTANT) -public: - QScxmlScxmlService(QScxmlStateMachine *stateMachine, - QScxmlStateMachine *parentStateMachine, - QObject *parent = nullptr); - - bool start(const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, - const QVector<QScxmlExecutableContent::StringId> &names) Q_DECL_OVERRIDE; - QString id() const Q_DECL_OVERRIDE; - QString name() const Q_DECL_OVERRIDE; - void postEvent(QScxmlEvent *event) Q_DECL_OVERRIDE; - - QScxmlStateMachine *stateMachine() const; -}; - -class Q_SCXML_EXPORT QScxmlScxmlServiceFactory: public QScxmlInvokableServiceFactory -{ -public: - QScxmlScxmlServiceFactory(const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::StringId> &names, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters); - -protected: - QScxmlScxmlService *invokeDynamic(QScxmlStateMachine *parentStateMachine, - const QString &sourceUrl); - QScxmlScxmlService *invokeStatic(QScxmlStateMachine *childStateMachine, - QScxmlStateMachine *parentStateMachine); -}; - -template<class T> -class QScxmlStaticScxmlServiceFactory: public QScxmlScxmlServiceFactory -{ + Q_DECLARE_PRIVATE(QScxmlStaticScxmlServiceFactory) public: QScxmlStaticScxmlServiceFactory( - const QScxmlExecutableContent::InvokeInfo &newInvokeInfo, - const QVector<QScxmlExecutableContent::StringId> &newNameList, - const QVector<QScxmlExecutableContent::ParameterInfo> &newParameters) - : QScxmlScxmlServiceFactory(newInvokeInfo, newNameList, newParameters) - {} - - QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) Q_DECL_OVERRIDE - { - return invokeStatic(new T, parentStateMachine); - } + const QMetaObject *metaObject, + const QScxmlExecutableContent::InvokeInfo &invokeInfo, + const QVector<QScxmlExecutableContent::StringId> &nameList, + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, + QObject *parent = nullptr); + + QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) Q_DECL_OVERRIDE; }; -class Q_SCXML_EXPORT QScxmlDynamicScxmlServiceFactory: public QScxmlScxmlServiceFactory +class Q_SCXML_EXPORT QScxmlDynamicScxmlServiceFactory: public QScxmlInvokableServiceFactory { + Q_OBJECT public: QScxmlDynamicScxmlServiceFactory( const QScxmlExecutableContent::InvokeInfo &invokeInfo, const QVector<QScxmlExecutableContent::StringId> &names, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters); + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, + QObject *parent = nullptr); QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) Q_DECL_OVERRIDE; }; diff --git a/src/scxml/qscxmlinvokableservice_p.h b/src/scxml/qscxmlinvokableservice_p.h index 52bd6d0..e2a9e7b 100644 --- a/src/scxml/qscxmlinvokableservice_p.h +++ b/src/scxml/qscxmlinvokableservice_p.h @@ -71,7 +71,7 @@ public: QScxmlStateMachine *parentStateMachine; }; -class QScxmlInvokableServiceFactoryPrivate +class QScxmlInvokableServiceFactoryPrivate : public QObjectPrivate { public: QScxmlInvokableServiceFactoryPrivate( @@ -79,23 +79,50 @@ public: const QVector<QScxmlExecutableContent::StringId> &names, const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters); - QString calculateSrcexpr(QScxmlStateMachine *parent, bool *ok) const; - QScxmlExecutableContent::InvokeInfo invokeInfo; QVector<QScxmlExecutableContent::StringId> names; QVector<QScxmlExecutableContent::ParameterInfo> parameters; }; -class QScxmlScxmlServicePrivate : public QScxmlInvokableServicePrivate +class Q_SCXML_EXPORT QScxmlScxmlService: public QScxmlInvokableService { + Q_OBJECT + Q_DECLARE_PRIVATE(QScxmlInvokableService) public: - QScxmlScxmlServicePrivate(QScxmlStateMachine *stateMachine, - QScxmlStateMachine *parentStateMachine); - ~QScxmlScxmlServicePrivate(); + QScxmlScxmlService(QScxmlStateMachine *stateMachine, + QScxmlStateMachine *parentStateMachine, + QScxmlInvokableServiceFactory *parent); + ~QScxmlScxmlService(); + + bool start() Q_DECL_OVERRIDE; + QString id() const Q_DECL_OVERRIDE; + QString name() const Q_DECL_OVERRIDE; + void postEvent(QScxmlEvent *event) Q_DECL_OVERRIDE; QScxmlStateMachine *stateMachine; }; +class QScxmlStaticScxmlServiceFactoryPrivate : public QScxmlInvokableServiceFactoryPrivate +{ +public: + QScxmlStaticScxmlServiceFactoryPrivate( + const QMetaObject *metaObject, + const QScxmlExecutableContent::InvokeInfo &invokeInfo, + const QVector<QScxmlExecutableContent::StringId> &names, + const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters); + + const QMetaObject *metaObject; +}; + +QScxmlScxmlService *invokeDynamicScxmlService(const QString &sourceUrl, + QScxmlStateMachine *parentStateMachine, + QScxmlInvokableServiceFactory *factory); +QScxmlScxmlService *invokeStaticScxmlService(QScxmlStateMachine *childStateMachine, + QScxmlStateMachine *parentStateMachine, + QScxmlInvokableServiceFactory *factory); +QString calculateSrcexpr(QScxmlStateMachine *parent, QScxmlExecutableContent::EvaluatorId srcexpr, + bool *ok); + QT_END_NAMESPACE #endif // QSCXMLINVOKABLESERVICE_P_H diff --git a/src/scxml/qscxmlnulldatamodel.cpp b/src/scxml/qscxmlnulldatamodel.cpp index 08a9333..3054383 100644 --- a/src/scxml/qscxmlnulldatamodel.cpp +++ b/src/scxml/qscxmlnulldatamodel.cpp @@ -84,8 +84,7 @@ public: ResolvedEvaluatorInfo prepare(QScxmlExecutableContent::EvaluatorId id) { - Q_Q(QScxmlNullDataModel); - auto td = q->tableData(); + auto td = m_stateMachine->tableData(); const QScxmlExecutableContent::EvaluatorInfo &info = td->evaluatorInfo(id); QString expr = td->string(info.expr); for (int i = 0; i < expr.size(); ) { @@ -123,19 +122,22 @@ private: * This class implements the null data model as described in the * \l {SCXML Specification - B.1 The Null Data Model}. Using the value \c "null" * for the \e datamodel attribute of the \c <scxml> element means that there is - * no underlying data model. + * no underlying data model, but some executable content, like \c In(...) or + * \c <log> can still be used. * * \sa QScxmlStateMachine QScxmlDataModel */ /*! - * Creates a new Qt SCXML data model, with the parent object \a parent. + * Creates a new Qt SCXML null data model, with the parent object \a parent. */ QScxmlNullDataModel::QScxmlNullDataModel(QObject *parent) : QScxmlDataModel(*(new QScxmlNullDataModelPrivate), parent) {} -/*! \internal */ +/*! + Destroys the data model. + */ QScxmlNullDataModel::~QScxmlNullDataModel() { } @@ -150,22 +152,43 @@ bool QScxmlNullDataModel::setup(const QVariantMap &initialDataValues) return true; } +/*! + \reimp + Evaluates the executable content pointed to by \a id and records in \a ok + whether there was an error. Returns the result of the evaluation as a string. + The null data model can evaluate the \c <log> element, so this might result in + an actual value, rather than an error + */ QString QScxmlNullDataModel::evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) { + Q_D(QScxmlNullDataModel); // We do implement this, because <log> is allowed in the Null data model, // and <log> has an expr attribute that needs "evaluation" for it to generate the log message. *ok = true; - auto td = tableData(); + auto td = d->m_stateMachine->tableData(); const QScxmlExecutableContent::EvaluatorInfo &info = td->evaluatorInfo(id); return td->string(info.expr); } +/*! + \reimp + Evaluates the executable content pointed to by \a id and records in \a ok + whether there was an error. Returns the result of the evaluation as a boolean + value. The null data model can evaluate the instruction \c In(...), so this + might result in an actual value, rather than an error. + */ bool QScxmlNullDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_D(QScxmlNullDataModel); return d->evalBool(id, ok); } +/*! + \reimp + Evaluates the executable content pointed to by \a id and records in \a ok + whether there was an error. As this is the null data model, any evaluation will in + fact result in an error, with \a ok set to \c false. Returns an empty QVariant. + */ QVariant QScxmlNullDataModel::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_UNUSED(id); @@ -176,6 +199,12 @@ QVariant QScxmlNullDataModel::evaluateToVariant(QScxmlExecutableContent::Evaluat return QVariant(); } +/*! + \reimp + Evaluates the executable content pointed to by \a id and records in \a ok + whether there was an error. As this is the null data model, any evaluation will in + fact result in an error, with \a ok set to \c false. + */ void QScxmlNullDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_UNUSED(id); @@ -185,6 +214,11 @@ void QScxmlNullDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id QStringLiteral("Cannot evaluate expressions on a null data model")); } +/*! + \reimp + Throws an error and sets \a ok to \c false, because the null data model cannot evaluate + assignments. + */ void QScxmlNullDataModel::evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_UNUSED(id); @@ -194,6 +228,11 @@ void QScxmlNullDataModel::evaluateAssignment(QScxmlExecutableContent::EvaluatorI QStringLiteral("Cannot assign values on a null data model")); } +/*! + \reimp + Throws an error and sets \a ok to \c false, because the null data model cannot + initialize data. + */ void QScxmlNullDataModel::evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) { Q_UNUSED(id); @@ -203,7 +242,13 @@ void QScxmlNullDataModel::evaluateInitialization(QScxmlExecutableContent::Evalua QStringLiteral("Cannot initialize values on a null data model")); } -bool QScxmlNullDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) +/*! + \reimp + Throws an error and sets \a ok to \c false, because the null data model cannot + evaluate \c <foreach> blocks. + */ +void QScxmlNullDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, + ForeachLoopBody *body) { Q_UNUSED(id); Q_UNUSED(body); @@ -211,11 +256,11 @@ bool QScxmlNullDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId i QScxmlStateMachinePrivate::get(stateMachine())->submitError( QStringLiteral("error.execution"), QStringLiteral("Cannot run foreach on a null data model")); - return false; } /*! * \reimp + * Does not actually set the \a event, because the null data model does not handle events. */ void QScxmlNullDataModel::setScxmlEvent(const QScxmlEvent &event) { diff --git a/src/scxml/qscxmlnulldatamodel.h b/src/scxml/qscxmlnulldatamodel.h index e4f7942..830162f 100644 --- a/src/scxml/qscxmlnulldatamodel.h +++ b/src/scxml/qscxmlnulldatamodel.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef NULLDATAMODEL_H -#define NULLDATAMODEL_H +#ifndef QSCXMLNULLDATAMODEL_H +#define QSCXMLNULLDATAMODEL_H #include <QtScxml/qscxmldatamodel.h> @@ -55,15 +55,13 @@ public: Q_INVOKABLE bool setup(const QVariantMap &initialDataValues) Q_DECL_OVERRIDE; -#ifndef Q_QDOC QString evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; bool evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; QVariant evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; void evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL; - bool evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) Q_DECL_OVERRIDE Q_DECL_FINAL; -#endif // Q_QDOC + void evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) Q_DECL_OVERRIDE Q_DECL_FINAL; void setScxmlEvent(const QScxmlEvent &event) Q_DECL_OVERRIDE; @@ -74,4 +72,4 @@ public: QT_END_NAMESPACE -#endif // NULLDATAMODEL_H +#endif // QSCXMLNULLDATAMODEL_H diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp index ef5a312..6bc7ab5 100644 --- a/src/scxml/qscxmlstatemachine.cpp +++ b/src/scxml/qscxmlstatemachine.cpp @@ -449,7 +449,7 @@ void QScxmlStateMachinePrivate::addService(int invokingState) continue; // service failed to start const QString serviceName = service->name(); m_invokedServices[size_t(id)] = { invokingState, service, serviceName }; - service->start(factory->invokeInfo(), factory->parameters(), factory->names()); + service->start(); } emitInvokedServicesChanged(); } @@ -533,7 +533,16 @@ void QScxmlStateMachinePrivate::postEvent(QScxmlEvent *event) auto factory = serviceFactory(id); if (event->invokeId() == service->id()) { setEvent(event); - service->finalize(factory->invokeInfo().finalize); + + const QScxmlExecutableContent::ContainerId finalize + = factory->invokeInfo().finalize; + if (finalize != QScxmlExecutableContent::NoContainer) { + auto psm = service->parentStateMachine(); + qCDebug(qscxmlLog) << psm << "running finalize on event"; + auto smp = QScxmlStateMachinePrivate::get(psm); + smp->m_executionEngine->execute(finalize); + } + resetEvent(); } if (factory->invokeInfo().autoforward) { @@ -580,36 +589,47 @@ void QScxmlStateMachinePrivate::submitDelayedEvent(QScxmlEvent *event) } /*! - * \internal - * \brief Submits an error event to the external event queue of this state machine. + * Submits an error event to the external event queue of this state machine. * - * \param type The error message type, e.g. "error.execution". The type has to start with "error.". - * \param msg A string describing the nature of the error. This is passed to the event as the - * errorMessage - * \param sendid The sendid of the message causing the error, if it has one. + * The type of the error is specified by \a type. The value of type has to begin + * with the string \e error. For example \c {error.execution}. The message, + * \a message, decribes the error and is passed to the event as the + * \c errorMessage property. The \a sendId of the message causing the error is specified, if it has + * one. */ -void QScxmlStateMachinePrivate::submitError(const QString &type, const QString &msg, const QString &sendid) +void QScxmlStateMachinePrivate::submitError(const QString &type, const QString &message, + const QString &sendId) { Q_Q(QScxmlStateMachine); - qCDebug(qscxmlLog) << q << "had error" << type << ":" << msg; + qCDebug(qscxmlLog) << q << "had error" << type << ":" << message; if (!type.startsWith(QStringLiteral("error."))) qCWarning(qscxmlLog) << q << "Message type of error message does not start with 'error.'!"; - q->submitEvent(QScxmlEventBuilder::errorEvent(q, type, msg, sendid)); + q->submitEvent(QScxmlEventBuilder::errorEvent(q, type, message, sendId)); } void QScxmlStateMachinePrivate::start() { + Q_Q(QScxmlStateMachine); + if (m_stateTable->binding == StateTable::LateBinding) m_isFirstStateEntry.resize(m_stateTable->stateCount, true); + bool running = isRunnable() && !isPaused(); m_runningState = Starting; Q_ASSERT(m_stateTable->initialTransition != StateTable::InvalidIndex); + + if (!running) + emit q->runningChanged(true); } void QScxmlStateMachinePrivate::pause() { - if (isRunnable() && !isPaused()) + Q_Q(QScxmlStateMachine); + + if (isRunnable() && !isPaused()) { m_runningState = Paused; + emit q->runningChanged(false); + } } void QScxmlStateMachinePrivate::processEvents() @@ -1096,7 +1116,10 @@ void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions m_executionEngine->execute(dhc); if (state.type == StateTable::State::Final) { if (state.parentIsScxmlElement()) { + bool running = isRunnable() && !isPaused(); m_runningState = Finished; + if (running) + emit q->runningChanged(false); } else { const auto &parent = m_stateTable->state(state.parent); m_executionEngine->execute(state.doneData, m_tableData->string(parent.name)); @@ -1532,6 +1555,16 @@ QScxmlStateMachine::QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *p \brief The loader that is currently used to resolve and load URIs for the state machine. */ +/*! + \property QScxmlStateMachine::tableData + + \brief The table data that is used when generating C++ from an SCXML file. + + The class implementing + the state machine will use this property to assign the generated table + data. The state machine does not assume ownership of the table data. + */ + QString QScxmlStateMachine::sessionId() const { Q_D(const QScxmlStateMachine); @@ -1602,13 +1635,6 @@ QScxmlCompiler::Loader *QScxmlStateMachine::loader() const return d->m_loader; } -/*! - * \internal - * - * This is used internally in order to execute the executable content. - * - * \return the data tables used by the state machine. - */ QScxmlTableData *QScxmlStateMachine::tableData() const { Q_D(const QScxmlStateMachine); @@ -1616,36 +1642,36 @@ QScxmlTableData *QScxmlStateMachine::tableData() const return d->m_tableData; } -/*! - * \internal - * This is used when generating C++ from an SCXML file. The class implementing the - * state machine will use this method to pass in the table data (which is also generated). - * - * \return the data tables used by the state machine. - */ void QScxmlStateMachine::setTableData(QScxmlTableData *tableData) { Q_D(QScxmlStateMachine); - Q_ASSERT(tableData); + + if (d->m_tableData == tableData) + return; d->m_tableData = tableData; - d->m_stateTable = reinterpret_cast<const QScxmlExecutableContent::StateTable *>( - tableData->stateMachineTable()); - if (objectName().isEmpty()) { - setObjectName(tableData->name()); - } - if (d->m_stateTable->maxServiceId != QScxmlExecutableContent::StateTable::InvalidIndex) { - const size_t serviceCount = size_t(d->m_stateTable->maxServiceId + 1); - d->m_invokedServices.resize(serviceCount, { -1, nullptr, QString() }); - d->m_cachedFactories.resize(serviceCount, nullptr); + if (tableData) { + d->m_stateTable = reinterpret_cast<const QScxmlExecutableContent::StateTable *>( + tableData->stateMachineTable()); + if (objectName().isEmpty()) { + setObjectName(tableData->name()); + } + if (d->m_stateTable->maxServiceId != QScxmlExecutableContent::StateTable::InvalidIndex) { + const size_t serviceCount = size_t(d->m_stateTable->maxServiceId + 1); + d->m_invokedServices.resize(serviceCount, { -1, nullptr, QString() }); + d->m_cachedFactories.resize(serviceCount, nullptr); + } + + if (d->m_stateTable->version != Q_QSCXMLC_OUTPUT_REVISION) { + qFatal("Cannot mix incompatible state table (version 0x%x) with this library " + "(version 0x%x)", d->m_stateTable->version, Q_QSCXMLC_OUTPUT_REVISION); + } + Q_ASSERT(tableData->stateMachineTable()[d->m_stateTable->arrayOffset + + d->m_stateTable->arraySize] + == QScxmlExecutableContent::StateTable::terminator); } - if (d->m_stateTable->version != Q_QSCXMLC_OUTPUT_REVISION) - qFatal("Cannot mix incompatible state table (version 0x%x) with this library (version 0x%x)", - d->m_stateTable->version, Q_QSCXMLC_OUTPUT_REVISION); - Q_ASSERT(tableData->stateMachineTable()[d->m_stateTable->arrayOffset + - d->m_stateTable->arraySize] - == QScxmlExecutableContent::StateTable::terminator); + emit tableDataChanged(tableData); } /*! @@ -2057,6 +2083,13 @@ void QScxmlStateMachine::stop() d->pause(); } +/*! + Returns \c true if the state with the ID \a stateIndex is active. + + This method is part of the interface to the compiled representation of SCXML + state machines. It should only be used internally and by state machines + compiled from SCXML documents. + */ bool QScxmlStateMachine::isActive(int stateIndex) const { Q_D(const QScxmlStateMachine); diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h index 2dea079..d4435f7 100644 --- a/src/scxml/qscxmlstatemachine.h +++ b/src/scxml/qscxmlstatemachine.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SCXMLSTATEMACHINE_H -#define SCXMLSTATEMACHINE_H +#ifndef QSCXMLSTATEMACHINE_H +#define QSCXMLSTATEMACHINE_H #include <QtScxml/qscxmldatamodel.h> #include <QtScxml/qscxmlexecutablecontent.h> @@ -75,12 +75,11 @@ class Q_SCXML_EXPORT QScxmlStateMachine: public QObject Q_PROPERTY(bool invoked READ isInvoked CONSTANT) Q_PROPERTY(QVector<QScxmlError> parseErrors READ parseErrors CONSTANT) Q_PROPERTY(QScxmlCompiler::Loader *loader READ loader WRITE setLoader NOTIFY loaderChanged) + Q_PROPERTY(QScxmlTableData *tableData READ tableData WRITE setTableData NOTIFY tableDataChanged) protected: -#ifndef Q_QDOC explicit QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent = nullptr); QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent = nullptr); -#endif // Q_QDOC public: static QScxmlStateMachine *fromFile(const QString &fileName); @@ -314,6 +313,9 @@ public: QVector<QScxmlInvokableService *> invokedServices() const; + QScxmlTableData *tableData() const; + void setTableData(QScxmlTableData *tableData); + Q_SIGNALS: void runningChanged(bool running); void invokedServicesChanged(const QVector<QScxmlInvokableService *> &invokedServices); @@ -324,6 +326,7 @@ Q_SIGNALS: void initialValuesChanged(const QVariantMap &initialValues); void initializedChanged(bool initialized); void loaderChanged(QScxmlCompiler::Loader *loader); + void tableDataChanged(QScxmlTableData *tableData); public Q_SLOTS: void start(); @@ -336,12 +339,8 @@ protected: // methods for friends: friend class QScxmlInvokableServicePrivate; friend class QScxmlExecutionEngine; -#ifndef Q_QDOC // The methods below are used by the compiled state machines. bool isActive(int stateIndex) const; - QScxmlTableData *tableData() const; - void setTableData(QScxmlTableData *tableData); -#endif // Q_QDOC private: QMetaObject::Connection connectToStateImpl(const QString &scxmlStateName, @@ -356,4 +355,4 @@ private: QT_END_NAMESPACE -#endif // SCXMLSTATEMACHINE_H +#endif // QSCXMLSTATEMACHINE_H diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h index 69f3be4..bfa7bc1 100644 --- a/src/scxml/qscxmlstatemachine_p.h +++ b/src/scxml/qscxmlstatemachine_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef SCXMLSTATEMACHINE_P_H -#define SCXMLSTATEMACHINE_P_H +#ifndef QSCXMLSTATEMACHINE_P_H +#define QSCXMLSTATEMACHINE_P_H // // W A R N I N G @@ -383,5 +383,5 @@ private: QT_END_NAMESPACE -#endif // SCXMLSTATEMACHINE_P_H +#endif // QSCXMLSTATEMACHINE_P_H diff --git a/src/scxml/qscxmltabledata.cpp b/src/scxml/qscxmltabledata.cpp index 2457840..f9665d3 100644 --- a/src/scxml/qscxmltabledata.cpp +++ b/src/scxml/qscxmltabledata.cpp @@ -43,6 +43,75 @@ QT_USE_NAMESPACE +/*! + \class QScxmlTableData + \since 5.8 + \inmodule QtScxml + \brief The QScxmlTableData class is used by compiled state machines. + + QScxmlTableData is the interface to the compiled representation of SCXML + state machines. It should only be used internally and by state machines + compiled from SCXML documents. + */ + +/*! + \fn QScxmlTableData::string(QScxmlExecutableContent::StringId id) const + Returns a QString for the given \a id. + */ + +/*! + \fn QScxmlTableData::instructions() const + Returns a pointer to the instructions of executable content contained in + the state machine. + */ + +/*! + \fn QScxmlTableData::evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const + Returns the QScxmlExecutableContent::EvaluatorInfo object for the given \a evaluatorId. + */ + +/*! + \fn QScxmlTableData::assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const + Returns the QScxmlExecutableContent::AssignmentInfo object for the given \a assignmentId. + */ + +/*! + \fn QScxmlTableData::foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const + Returns the QScxmlExecutableContent::ForeachInfo object for the given \a foreachId. + */ + +/*! + \fn QScxmlTableData::dataNames(int *count) const + Retrieves the string IDs for the names of data items in the data model. The + number of strings is saved into \a count and a pointer to an array of + string IDs is returned. + + Returns a pointer to an array of string IDs. + */ + +/*! + \fn QScxmlTableData::initialSetup() const + Initializes the table data. Returns the ID of the container with + instructions to be executed when initializing the state machine. + */ + +/*! + \fn QScxmlTableData::name() const + Returns the name of the state machine. + */ + +/*! + \fn QScxmlTableData::stateMachineTable() const + Returns a pointer to the complete state table, expressed as an opaque + sequence of integers. + */ + +/*! + \fn QScxmlTableData::serviceFactory(int id) const + Returns the service factory that creates invokable services for the state + with the ID \a id. + */ + using namespace QScxmlInternal; namespace { @@ -68,7 +137,7 @@ public: { m_activeSequences.reserve(4); - tableData.theInitialSetup = QScxmlExecutableContent::NoInstruction; + tableData.theInitialSetup = QScxmlExecutableContent::NoContainer; } void buildTableData(DocumentModel::ScxmlDocument *doc) @@ -264,7 +333,7 @@ protected: // visitor params.append(p); } QScxmlExecutableContent::ContainerId finalize = - QScxmlExecutableContent::NoInstruction; + QScxmlExecutableContent::NoContainer; if (!invoke->finalize.isEmpty()) { finalize = startNewSequence(); visit(&invoke->finalize); @@ -528,7 +597,7 @@ protected: ContainerId generate(const DocumentModel::InstructionSequences &inSequences) { if (inSequences.isEmpty()) - return NoInstruction; + return NoContainer; auto id = m_instructions.newContainerId(); auto outSequences = m_instructions.add<InstructionSequences>(); @@ -898,6 +967,10 @@ private: } // anonymous namespace +/*! + \fn QScxmlTableData::~QScxmlTableData() + Destroys the SXCML table data. + */ QScxmlTableData::~QScxmlTableData() {} diff --git a/src/scxml/qscxmltabledata_p.h b/src/scxml/qscxmltabledata_p.h index 2c825db..767b88a 100644 --- a/src/scxml/qscxmltabledata_p.h +++ b/src/scxml/qscxmltabledata_p.h @@ -117,7 +117,7 @@ public: QVector<QScxmlExecutableContent::AssignmentInfo> theAssignments; QVector<QScxmlExecutableContent::ForeachInfo> theForeaches; QVector<QScxmlExecutableContent::StringId> theDataNameIds; - QScxmlExecutableContent::EvaluatorId theInitialSetup; + QScxmlExecutableContent::ContainerId theInitialSetup; int theName; }; } // QScxmlInternal namespace diff --git a/sync.profile b/sync.profile index acbc753..1a8e936 100644 --- a/sync.profile +++ b/sync.profile @@ -7,15 +7,3 @@ ); %deprecatedheaders = ( ); -# Module dependencies. -# Every module that is required to build this module should have one entry. -# Each of the module version specifiers can take one of the following values: -# - A specific Git revision. -# - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) -# - an empty string to use the same branch under test (dependencies will become "refs/heads/master" if we are in the master branch) -# -%dependencies = ( - "qtbase" => "", - "qtdeclarative" => "", - "qtxmlpatterns" => "", -); diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp index f09ad42..99d6661 100644 --- a/tests/auto/statemachine/tst_statemachine.cpp +++ b/tests/auto/statemachine/tst_statemachine.cpp @@ -51,6 +51,7 @@ private Q_SLOTS: void eventOccurred(); void doneDotStateEvent(); + void running(); }; void tst_StateMachine::stateNames_data() @@ -365,6 +366,26 @@ void tst_StateMachine::doneDotStateEvent() QVERIFY(stateMachine->activeStateNames(true).contains(QLatin1String("success"))); } +void tst_StateMachine::running() +{ + QScopedPointer<QScxmlStateMachine> stateMachine( + QScxmlStateMachine::fromFile(QString(":/tst_statemachine/statenames.scxml"))); + QVERIFY(!stateMachine.isNull()); + + QSignalSpy runningChangedSpy(stateMachine.data(), SIGNAL(runningChanged(bool))); + + QCOMPARE(stateMachine->isRunning(), false); + + stateMachine->start(); + + QCOMPARE(runningChangedSpy.count(), 1); + QCOMPARE(stateMachine->isRunning(), true); + + stateMachine->stop(); + + QCOMPARE(runningChangedSpy.count(), 2); + QCOMPARE(stateMachine->isRunning(), false); +} QTEST_MAIN(tst_StateMachine) diff --git a/tools/qscxmlc/decl.t b/tools/qscxmlc/decl.t index 482c123..baf1600 100644 --- a/tools/qscxmlc/decl.t +++ b/tools/qscxmlc/decl.t @@ -5,7 +5,7 @@ class ${classname}: public QScxmlStateMachine ${properties} public: - ${classname}(QObject *parent = 0); + Q_INVOKABLE ${classname}(QObject *parent = 0); ~${classname}(); ${accessors} diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index d99b3b0..40ae1dd 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -374,8 +374,8 @@ int createFactoryId(QStringList &factories, const QString &className, const int idx = factories.size(); QString line = QStringLiteral("case %1: return new ").arg(QString::number(idx)); - if (invokeInfo.expr == QScxmlExecutableContent::NoInstruction) { - line += QStringLiteral("QScxmlStaticScxmlServiceFactory< %1::%2 >(") + if (invokeInfo.expr == QScxmlExecutableContent::NoEvaluator) { + line += QStringLiteral("QScxmlStaticScxmlServiceFactory(&%1::%2::staticMetaObject,") .arg(namespacePrefix, className); } else { line += QStringLiteral("QScxmlDynamicScxmlServiceFactory("); @@ -445,7 +445,7 @@ void CppDumper::dump(TranslationUnit *unit) const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int { QString className; - if (invokeInfo.expr == QScxmlExecutableContent::NoInstruction) { + if (invokeInfo.expr == QScxmlExecutableContent::NoEvaluator) { className = mangleIdentifier(classnameForDocument.value(content.data())); } return createFactoryId(factories[i], className, namespacePrefix, @@ -755,6 +755,20 @@ QString CppDumper::generateMetaObject(const QString &className, classDef.qualified = classDef.classname; classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public); classDef.hasQObject = true; + FunctionDef constructor; + constructor.name = className.toUtf8(); + constructor.access = FunctionDef::Public; + constructor.isInvokable = true; + constructor.isConstructor = true; + + ArgumentDef arg; + arg.type.name = "QObject *"; + arg.type.rawName = arg.type.name; + arg.normalizedType = arg.type.name; + arg.name = "parent"; + arg.typeNameForCast = arg.type.name + "*"; + constructor.arguments.append(arg); + classDef.constructorList.append(constructor); // stateNames: int stateIdx = 0; diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h index 9c8cfe8..a987f80 100644 --- a/tools/qscxmlc/scxmlcppdumper.h +++ b/tools/qscxmlc/scxmlcppdumper.h @@ -26,8 +26,8 @@ ** ****************************************************************************/ -#ifndef CPPDUMPER_H -#define CPPDUMPER_H +#ifndef SCXMLCPPDUMPER_H +#define SCXMLCPPDUMPER_H #include "qscxmlglobals.h" @@ -99,4 +99,4 @@ private: QT_END_NAMESPACE -#endif // CPPDUMPER_H +#endif // SCXMLCPPDUMPER_H diff --git a/tools/tools.pro b/tools/tools.pro index f96987d..1854cfc 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -1,4 +1,2 @@ TEMPLATE = subdirs SUBDIRS = qscxmlc - -qscxmlc.CONFIG = host_build |