diff options
author | Jarek Kobus <jaroslaw.kobus@theqtcompany.com> | 2016-05-31 16:21:29 +0200 |
---|---|---|
committer | Jarek Kobus <jaroslaw.kobus@qt.io> | 2016-06-07 08:43:43 +0000 |
commit | 4a0d14bfcc49edc60127e0833f6cc8d4edad9027 (patch) | |
tree | 250b9730d25a9c01a4563ad25e18445f6e11e59d | |
parent | 2906a351df804c0e8d5e4377b9ecd48d293ddf41 (diff) |
Implement srcexpr in <invoke>
Change-Id: I572342eb4952a6a0777a7503d374da6536ba052d
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/scxml/qscxmlinvokableservice.cpp | 55 | ||||
-rw-r--r-- | src/scxml/qscxmlinvokableservice.h | 11 | ||||
-rw-r--r-- | src/scxml/qscxmlparser.cpp | 66 | ||||
-rw-r--r-- | src/scxml/qscxmlparser_p.h | 2 | ||||
-rw-r--r-- | src/scxml/qscxmlqstates.h | 20 | ||||
-rw-r--r-- | src/scxml/qscxmlstatemachine.cpp | 16 | ||||
-rw-r--r-- | src/scxml/qscxmlstatemachine.h | 5 | ||||
-rw-r--r-- | src/scxml/qscxmlstatemachine_p.h | 2 | ||||
-rw-r--r-- | tests/3rdparty/scion-tests/scxml-test-framework/test/test216sub1.scxml | 5 | ||||
-rw-r--r-- | tests/auto/scion/scion.pro | 1 | ||||
-rw-r--r-- | tests/auto/scion/tst_scion.cpp | 5 | ||||
-rw-r--r-- | tools/qscxmlc/scxmlcppdumper.cpp | 3 |
12 files changed, 175 insertions, 16 deletions
diff --git a/src/scxml/qscxmlinvokableservice.cpp b/src/scxml/qscxmlinvokableservice.cpp index d473915..bb29635 100644 --- a/src/scxml/qscxmlinvokableservice.cpp +++ b/src/scxml/qscxmlinvokableservice.cpp @@ -97,12 +97,17 @@ QScxmlInvokableServiceFactory *QScxmlInvokableService::service() const class QScxmlInvokableServiceFactory::Data { public: - Data(QScxmlExecutableContent::StringId invokeLocation, QScxmlExecutableContent::StringId id, - QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, - const QVector<QScxmlExecutableContent::StringId> &namelist, bool autoforward, + Data(QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, + QScxmlExecutableContent::StringId id, + QScxmlExecutableContent::StringId idPrefix, + QScxmlExecutableContent::StringId idlocation, + const QVector<QScxmlExecutableContent::StringId> &namelist, + bool autoforward, const QVector<QScxmlInvokableServiceFactory::Param> ¶ms, QScxmlExecutableContent::ContainerId finalize) : invokeLocation(invokeLocation) + , srcexpr(srcexpr) , id(id) , idPrefix(idPrefix) , idlocation(idlocation) @@ -113,6 +118,7 @@ public: {} QScxmlExecutableContent::StringId invokeLocation; + QScxmlExecutableContent::EvaluatorId srcexpr; QScxmlExecutableContent::StringId id; QScxmlExecutableContent::StringId idPrefix; QScxmlExecutableContent::StringId idlocation; @@ -123,11 +129,24 @@ public: }; QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory( - QScxmlExecutableContent::StringId invokeLocation, QScxmlExecutableContent::StringId id, - QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, - const QVector<QScxmlExecutableContent::StringId> &namelist, bool autoforward, - const QVector<Param> ¶ms, QScxmlExecutableContent::ContainerId finalize) - : d(new Data(invokeLocation, id, idPrefix, idlocation, namelist, autoforward, params, finalize)) + QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, + QScxmlExecutableContent::StringId id, + QScxmlExecutableContent::StringId idPrefix, + QScxmlExecutableContent::StringId idlocation, + const QVector<QScxmlExecutableContent::StringId> &namelist, + bool autoforward, + const QVector<Param> ¶ms, + QScxmlExecutableContent::ContainerId finalize) + : d(new Data(invokeLocation, + srcexpr, + id, + idPrefix, + idlocation, + namelist, + autoforward, + params, + finalize)) {} QScxmlInvokableServiceFactory::~QScxmlInvokableServiceFactory() @@ -135,6 +154,23 @@ QScxmlInvokableServiceFactory::~QScxmlInvokableServiceFactory() delete d; } +QString QScxmlInvokableServiceFactory::calculateSrcexpr(QScxmlStateMachine *parent, bool *ok) const +{ + Q_ASSERT(ok); + *ok = true; + auto dataModel = parent->dataModel(); + + if (d->srcexpr != QScxmlExecutableContent::NoEvaluator) { + *ok = false; + auto v = dataModel->evaluateToString(d->srcexpr, ok); + if (!*ok) + return QString(); + return v; + } + + return QString(); +} + QString QScxmlInvokableServiceFactory::calculateId(QScxmlStateMachine *parent, bool *ok) const { Q_ASSERT(ok); @@ -284,6 +320,7 @@ QScxmlStateMachine *QScxmlInvokableScxml::stateMachine() const QScxmlInvokableScxmlServiceFactory::QScxmlInvokableScxmlServiceFactory( QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, QScxmlExecutableContent::StringId id, QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, @@ -291,7 +328,7 @@ QScxmlInvokableScxmlServiceFactory::QScxmlInvokableScxmlServiceFactory( bool doAutoforward, const QVector<QScxmlInvokableServiceFactory::Param> ¶ms, QScxmlExecutableContent::ContainerId finalize) - : QScxmlInvokableServiceFactory(invokeLocation, id, idPrefix, idlocation, namelist, + : QScxmlInvokableServiceFactory(invokeLocation, srcexpr, id, idPrefix, idlocation, namelist, doAutoforward, params, finalize) {} diff --git a/src/scxml/qscxmlinvokableservice.h b/src/scxml/qscxmlinvokableservice.h index 21ba707..850047a 100644 --- a/src/scxml/qscxmlinvokableservice.h +++ b/src/scxml/qscxmlinvokableservice.h @@ -100,6 +100,7 @@ public: }; QScxmlInvokableServiceFactory(QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, QScxmlExecutableContent::StringId id, QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, @@ -112,6 +113,7 @@ public: virtual QScxmlInvokableService *invoke(QScxmlStateMachine *parent) = 0; public: // callbacks from the service: + QString calculateSrcexpr(QScxmlStateMachine *parent, bool *ok) const; QString calculateId(QScxmlStateMachine *parent, bool *ok) const; QVariantMap calculateData(QScxmlStateMachine *parent, bool *ok) const; bool autoforward() const; @@ -145,6 +147,7 @@ class Q_SCXML_EXPORT QScxmlInvokableScxmlServiceFactory: public QScxmlInvokableS { public: QScxmlInvokableScxmlServiceFactory(QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, QScxmlExecutableContent::StringId id, QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, @@ -154,7 +157,13 @@ public: QScxmlExecutableContent::ContainerId finalize); protected: - QScxmlInvokableService *finishInvoke(QScxmlStateMachine *child, QScxmlStateMachine *parent); +#ifndef BUILD_QSCXMLC + + QScxmlInvokableService *loadAndInvokeDynamically(QScxmlStateMachine *parent, + const QString &sourceUrl); +#endif // BUILD_QSCXMLC + QScxmlInvokableService *finishInvoke(QScxmlStateMachine *child, + QScxmlStateMachine *parent); }; QT_END_NAMESPACE diff --git a/src/scxml/qscxmlparser.cpp b/src/scxml/qscxmlparser.cpp index 55b1368..c22d617 100644 --- a/src/scxml/qscxmlparser.cpp +++ b/src/scxml/qscxmlparser.cpp @@ -1005,6 +1005,7 @@ class InvokeDynamicScxmlFactory: public QScxmlInvokableScxmlServiceFactory { public: InvokeDynamicScxmlFactory(QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, QScxmlExecutableContent::StringId id, QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, @@ -1012,7 +1013,15 @@ public: bool autoforward, const QVector<Param> ¶ms, QScxmlExecutableContent::ContainerId finalize) - : QScxmlInvokableScxmlServiceFactory(invokeLocation, id, idPrefix, idlocation, namelist, autoforward, params, finalize) + : QScxmlInvokableScxmlServiceFactory(invokeLocation, + srcexpr, + id, + idPrefix, + idlocation, + namelist, + autoforward, + params, + finalize) {} void setContent(const QSharedPointer<DocumentModel::ScxmlDocument> &content) @@ -1184,6 +1193,9 @@ private: endSequence(); } auto factory = new InvokeDynamicScxmlFactory(ctxt, + createEvaluatorString(QStringLiteral("invoke"), + QStringLiteral("srcexpr"), + invoke->srcexpr), addString(invoke->id), addString(node->id + QStringLiteral(".session-")), addString(invoke->idLocation), @@ -1363,6 +1375,14 @@ private: inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke(QScxmlStateMachine *parent) { + bool ok = true; + auto srcexpr = calculateSrcexpr(parent, &ok); + if (!ok) + return Q_NULLPTR; + + if (!srcexpr.isEmpty()) + return loadAndInvokeDynamically(parent, srcexpr); + auto child = QStateMachineBuilder().build(m_content.data()); auto dm = QScxmlDataModelPrivate::instantiateDataModel(m_content->root->dataModel); @@ -1375,6 +1395,50 @@ inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke(QScxmlStateMach } // anonymous namespace +#ifndef BUILD_QSCXMLC +QScxmlInvokableService *QScxmlInvokableScxmlServiceFactory::loadAndInvokeDynamically(QScxmlStateMachine *parent, + const QString &sourceUrl) +{ + QScxmlParser::Loader *loader = parent->loader(); + + QStringList errs; + const QByteArray data = loader->load(sourceUrl, sourceUrl.isEmpty() ? + QString() : QFileInfo(sourceUrl).path(), &errs); + + if (!errs.isEmpty()) { + qWarning() << errs; + return Q_NULLPTR; + } + + QXmlStreamReader reader(data); + QScxmlParser parser(&reader); + parser.setFileName(sourceUrl); + parser.setLoader(parent->loader()); + parser.parse(); + if (!parser.errors().isEmpty()) { + foreach (const QScxmlError &error, parser.errors()) + qWarning() << error.toString(); + return Q_NULLPTR; + } + + auto mainDoc = QScxmlParserPrivate::get(&parser)->scxmlDocument(); + if (mainDoc == nullptr) { + Q_ASSERT(!parser.errors().isEmpty()); + foreach (const QScxmlError &error, parser.errors()) + qWarning() << error.toString(); + return Q_NULLPTR; + } + + auto child = QStateMachineBuilder().build(mainDoc); + + auto dm = QScxmlDataModelPrivate::instantiateDataModel(mainDoc->root->dataModel); + dm->setParent(child); + child->setDataModel(dm); + + return finishInvoke(child, parent); +} +#endif // BUILD_QSCXMLC + /*! * \class QScxmlParser * \brief The QScxmlParser class is a parser for SCXML files. diff --git a/src/scxml/qscxmlparser_p.h b/src/scxml/qscxmlparser_p.h index 98867a8..42b61f8 100644 --- a/src/scxml/qscxmlparser_p.h +++ b/src/scxml/qscxmlparser_p.h @@ -702,6 +702,7 @@ private: static QStringList optionalAttributes(Kind kind); }; +public: class DefaultLoader: public QScxmlParser::Loader { public: @@ -711,6 +712,7 @@ private: QStringList *errors) Q_DECL_OVERRIDE Q_DECL_FINAL; }; +private: bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlParserPrivate::ParserState::Kind kind); ParserState ¤t(); ParserState &previous(); diff --git a/src/scxml/qscxmlqstates.h b/src/scxml/qscxmlqstates.h index 7280a0f..7014861 100644 --- a/src/scxml/qscxmlqstates.h +++ b/src/scxml/qscxmlqstates.h @@ -55,6 +55,7 @@ class QScxmlInvokeScxmlFactory: public QScxmlInvokableScxmlServiceFactory { public: QScxmlInvokeScxmlFactory(QScxmlExecutableContent::StringId invokeLocation, + QScxmlExecutableContent::EvaluatorId srcexpr, QScxmlExecutableContent::StringId id, QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation, @@ -62,12 +63,27 @@ public: bool doAutoforward, const QVector<Param> ¶ms, QScxmlExecutableContent::ContainerId finalize) - : QScxmlInvokableScxmlServiceFactory(invokeLocation, id, idPrefix, idlocation, namelist, - doAutoforward, params, finalize) + : QScxmlInvokableScxmlServiceFactory(invokeLocation, + srcexpr, + id, + idPrefix, + idlocation, + namelist, + doAutoforward, + params, + finalize) {} QScxmlInvokableService *invoke(QScxmlStateMachine *parent) Q_DECL_OVERRIDE { + bool ok = true; + auto srcexpr = calculateSrcexpr(parent, &ok); + if (!ok) + return Q_NULLPTR; + + if (!srcexpr.isEmpty()) + return loadAndInvokeDynamically(parent, srcexpr); + return finishInvoke(new T, parent); } }; diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp index 6603703..2b25eb9 100644 --- a/src/scxml/qscxmlstatemachine.cpp +++ b/src/scxml/qscxmlstatemachine.cpp @@ -234,6 +234,7 @@ QScxmlStateMachinePrivate::QScxmlStateMachinePrivate() , m_isInvoked(false) , m_isInitialized(false) , m_dataModel(Q_NULLPTR) + , m_loader(&m_defaultLoader) , m_dataBinding(QScxmlStateMachine::EarlyBinding) , m_executionEngine(Q_NULLPTR) , m_tableData(Q_NULLPTR) @@ -597,6 +598,21 @@ QScxmlDataModel *QScxmlStateMachine::dataModel() const return d->m_dataModel; } + +void QScxmlStateMachine::setLoader(QScxmlParser::Loader *loader) +{ + Q_D(QScxmlStateMachine); + + d->m_loader = loader; +} + +QScxmlParser::Loader *QScxmlStateMachine::loader() const +{ + Q_D(const QScxmlStateMachine); + + return d->m_loader; +} + /*! * \internal * Sets the binding method to the specified value. diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h index 5a800d5..2ab7880 100644 --- a/src/scxml/qscxmlstatemachine.h +++ b/src/scxml/qscxmlstatemachine.h @@ -44,6 +44,7 @@ #include <QtScxml/qscxmlexecutablecontent.h> #include <QtScxml/qscxmlerror.h> #include <QtScxml/qscxmlevent.h> +#include <QtScxml/qscxmlparser.h> #include <QString> #include <QVector> @@ -57,7 +58,6 @@ class QTextStream; class QScxmlEventBuilder; class QScxmlInvokableServiceFactory; class QScxmlInvokableService; -class QScxmlParser; class QScxmlStateMachine; class QScxmlTableData; @@ -105,6 +105,9 @@ public: void setDataModel(QScxmlDataModel *model); QScxmlDataModel *dataModel() const; + void setLoader(QScxmlParser::Loader *loader); + QScxmlParser::Loader *loader() const; + BindingMethod dataBinding() const; bool isRunning() const; diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h index 99334cf..ae68ce5 100644 --- a/src/scxml/qscxmlstatemachine_p.h +++ b/src/scxml/qscxmlstatemachine_p.h @@ -142,6 +142,8 @@ public: // types & data fields: bool m_isInitialized; QVariantMap m_initialValues; QScxmlDataModel *m_dataModel; + QScxmlParserPrivate::DefaultLoader m_defaultLoader; + QScxmlParser::Loader *m_loader; QScxmlStateMachine::BindingMethod m_dataBinding; QScxmlExecutableContent::QScxmlExecutionEngine *m_executionEngine; QScxmlTableData *m_tableData; diff --git a/tests/3rdparty/scion-tests/scxml-test-framework/test/test216sub1.scxml b/tests/3rdparty/scion-tests/scxml-test-framework/test/test216sub1.scxml new file mode 100644 index 0000000..cf9a3c4 --- /dev/null +++ b/tests/3rdparty/scion-tests/scxml-test-framework/test/test216sub1.scxml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?><!-- when invoked, terminate returning done.invoke. This proves that the invocation succeeded. --><scxml xmlns="http://www.w3.org/2005/07/scxml" xmlns:conf="http://www.w3.org/2005/scxml-conformance" initial="final" version="1.0" datamodel="ecmascript"> + +<final id="final"/> + +</scxml> diff --git a/tests/auto/scion/scion.pro b/tests/auto/scion/scion.pro index eccc02d..1b58a21 100644 --- a/tests/auto/scion/scion.pro +++ b/tests/auto/scion/scion.pro @@ -52,6 +52,7 @@ ALLSCXMLS = $$files($$SCXMLS_DIR/*.scxml, true) # For a better explanation about the "blacklisted" tests, see tst_scion.cpp # <invoke> BLACKLISTED = \ + test216sub1.scxml \ test226sub1.txml \ test239sub1.scxml \ test242sub1.scxml \ diff --git a/tests/auto/scion/tst_scion.cpp b/tests/auto/scion/tst_scion.cpp index b5e0d0f..c06ac1b 100644 --- a/tests/auto/scion/tst_scion.cpp +++ b/tests/auto/scion/tst_scion.cpp @@ -67,8 +67,6 @@ static QSet<QString> testFailOnRun = QSet<QString>() << QLatin1String("w3c-ecma/test456.txml") // replaced by modified_test456 // FIXME: qscxmlc fails on improper scxml file, currently no way of testing it properly for compiled case << QLatin1String("w3c-ecma/test301.txml") - // FIXME: Currently we do not support loading scripts from a srcexpr. - << QLatin1String("w3c-ecma/test216.txml") // FIXME: Currently we do not support nested scxml as a child of assign. << QLatin1String("w3c-ecma/test530.txml") ; @@ -217,6 +215,7 @@ void TestScion::dynamic() QScopedPointer<QScxmlStateMachine> stateMachine(parser.instantiateStateMachine()); QVERIFY(stateMachine != Q_NULLPTR); + stateMachine->setLoader(&loader); parser.instantiateDataModel(stateMachine.data()); const bool runResult = runTest(stateMachine.data(), testDescription.object()); @@ -262,6 +261,8 @@ void TestScion::compiled() QEXPECT_FAIL("", "This is expected to fail", Abort); } QVERIFY(stateMachine != Q_NULLPTR); + DynamicLoader loader; + stateMachine->setLoader(&loader); const bool runResult = runTest(stateMachine.data(), testDescription.object()); if (runResult == false && testStatus == TestFailsOnRun) diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index a48a6b6..6efa5f6 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -400,6 +400,9 @@ protected: Invoke *invoke = node->invokes.at(i); QString line = QStringLiteral("new QScxmlInvokeScxmlFactory<%1>(").arg(scxmlClassName(invoke->content.data())); line += QStringLiteral("%1, ").arg(Builder::createContext(QStringLiteral("invoke"))); + line += QStringLiteral("%1, ").arg(createEvaluatorString(QStringLiteral("invoke"), + QStringLiteral("srcexpr"), + invoke->srcexpr)); line += QStringLiteral("%1, ").arg(addString(invoke->id)); line += QStringLiteral("%1, ").arg(addString(node->id + QStringLiteral(".session-"))); line += QStringLiteral("%1, ").arg(addString(invoke->idLocation)); |