summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes3
-rw-r--r--.qmake.conf2
-rw-r--r--.tag1
-rw-r--r--examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc11
-rw-r--r--examples/scxml/calculator-common/statemachine.scxml2
-rw-r--r--examples/scxml/calculator-qml/calculator-qml.pro2
-rw-r--r--examples/scxml/calculator-qml/calculator-qml.qml7
-rw-r--r--examples/scxml/calculator-widgets/calculator-widgets.pro2
-rw-r--r--examples/scxml/calculator-widgets/doc/src/calculator.qdoc13
-rw-r--r--examples/scxml/calculator-widgets/mainwindow.cpp8
-rw-r--r--examples/scxml/ftpclient/doc/images/ftpclient-statechart.pngbin0 -> 19498 bytes
-rw-r--r--examples/scxml/ftpclient/doc/src/ftpclient.qdoc143
-rw-r--r--examples/scxml/ftpclient/ftpclient.pro18
-rw-r--r--examples/scxml/ftpclient/ftpcontrolchannel.cpp (renamed from tests/auto/qscxmlc/data/invalidInvoke2.scxml)72
-rw-r--r--examples/scxml/ftpclient/ftpcontrolchannel.h (renamed from tests/auto/qscxmlc/data/invalidInvoke3.scxml)bin3411 -> 3440 bytes
-rw-r--r--examples/scxml/ftpclient/ftpdatachannel.cpp (renamed from tests/auto/qscxmlc/data/invalidInvoke.scxml)72
-rw-r--r--examples/scxml/ftpclient/ftpdatachannel.h (renamed from tests/auto/qscxmlc/data/invalidContent.scxml)70
-rw-r--r--examples/scxml/ftpclient/main.cpp129
-rw-r--r--examples/scxml/ftpclient/simpleftp.scxml102
-rw-r--r--examples/scxml/invoke-common/MainView.qml16
-rw-r--r--examples/scxml/invoke-common/SubView.qml4
-rw-r--r--examples/scxml/invoke-common/statemachine.scxml1
-rw-r--r--examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc2
-rw-r--r--examples/scxml/invoke-dynamic/invoke-dynamic.qml6
-rw-r--r--examples/scxml/invoke-static/doc/src/invoke-static.qdoc10
-rw-r--r--examples/scxml/invoke-static/invoke-static.pro2
-rw-r--r--examples/scxml/mediaplayer-common/Mediaplayer.qml35
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc9
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-dynamic.qdocinc15
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc2
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc16
-rw-r--r--examples/scxml/mediaplayer-common/mainwindow.cpp19
-rw-r--r--examples/scxml/mediaplayer-common/mainwindow.h15
-rw-r--r--examples/scxml/mediaplayer-common/mediaplayer.scxml5
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc2
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml5
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro2
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc16
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro2
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml2
-rw-r--r--examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro2
-rw-r--r--examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc19
-rw-r--r--examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp9
-rw-r--r--examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc6
-rw-r--r--examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp9
-rw-r--r--examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro2
-rw-r--r--examples/scxml/pinball/doc/src/pinball.qdoc60
-rw-r--r--examples/scxml/pinball/mainwindow.cpp31
-rw-r--r--examples/scxml/pinball/mainwindow.h10
-rw-r--r--examples/scxml/pinball/pinball.pro2
-rw-r--r--examples/scxml/pinball/pinball.scxml123
-rw-r--r--examples/scxml/scxml.pro6
-rw-r--r--examples/scxml/sudoku/data/nearly-solved-sudoku.data9
-rw-r--r--examples/scxml/sudoku/data/sudoku.data9
-rw-r--r--examples/scxml/sudoku/doc/images/sudoku.pngbin0 -> 18609 bytes
-rw-r--r--examples/scxml/sudoku/doc/src/sudoku.qdoc394
-rw-r--r--examples/scxml/sudoku/main.cpp (renamed from tests/auto/qscxmlc/data/invalidRoot2.scxml)44
-rw-r--r--examples/scxml/sudoku/mainwindow.cpp238
-rw-r--r--examples/scxml/sudoku/mainwindow.h (renamed from tests/auto/qscxmlc/data/noContentInInvoke.scxml)60
-rw-r--r--examples/scxml/sudoku/sudoku.js130
-rw-r--r--examples/scxml/sudoku/sudoku.pro19
-rw-r--r--examples/scxml/sudoku/sudoku.qrc8
-rw-r--r--examples/scxml/sudoku/sudoku.scxml111
-rw-r--r--examples/scxml/trafficlight-common/Lights.ui.qml140
-rw-r--r--examples/scxml/trafficlight-common/TrafficLight.qml56
-rw-r--r--examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc8
-rw-r--r--examples/scxml/trafficlight-common/statemachine.scxml1
-rw-r--r--examples/scxml/trafficlight-common/trafficlight.cpp10
-rw-r--r--examples/scxml/trafficlight-common/trafficlight.h2
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc7
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp9
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro2
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml6
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc1
-rw-r--r--examples/scxml/trafficlight-qml-simple/Light.qml (renamed from tests/auto/qscxmlc/data/noContentInInvoke3.scxml)bin3396 -> 2693 bytes
-rw-r--r--examples/scxml/trafficlight-qml-simple/TrafficLight.qml (renamed from tests/auto/qscxmlc/data/badInitial.scxml)91
-rw-r--r--examples/scxml/trafficlight-qml-simple/doc/images/trafficlight.pngbin0 -> 4522 bytes
-rw-r--r--examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc63
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp (renamed from tests/auto/qscxmlc/data/noContentInInvoke2.scxml)50
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro14
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc6
-rw-r--r--examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc7
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro2
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc1
-rw-r--r--examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp4
-rw-r--r--examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.pro2
-rw-r--r--examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.pro2
-rw-r--r--mkspecs/features/qscxmlc.prf3
-rw-r--r--src/imports/scxmlstatemachine/eventconnection.cpp104
-rw-r--r--src/imports/scxmlstatemachine/eventconnection_p.h96
-rw-r--r--src/imports/scxmlstatemachine/invokedservices.cpp100
-rw-r--r--src/imports/scxmlstatemachine/invokedservices_p.h92
-rw-r--r--src/imports/scxmlstatemachine/plugin.cpp11
-rw-r--r--src/imports/scxmlstatemachine/plugins.qmltypes126
-rw-r--r--src/imports/scxmlstatemachine/scxmlstatemachine.pro11
-rw-r--r--src/imports/scxmlstatemachine/statemachineextended.cpp57
-rw-r--r--src/imports/scxmlstatemachine/statemachineextended_p.h76
-rw-r--r--src/imports/scxmlstatemachine/statemachineloader.cpp61
-rw-r--r--src/imports/scxmlstatemachine/statemachineloader_p.h (renamed from src/imports/scxmlstatemachine/statemachineloader.h)29
-rw-r--r--src/scxml/Qt5ScxmlConfigExtras.cmake.in51
-rw-r--r--src/scxml/Qt5ScxmlMacros.cmake67
-rw-r--r--src/scxml/doc/external-resources.qdoc5
-rw-r--r--src/scxml/doc/qtscxml-examples.qdoc4
-rw-r--r--src/scxml/doc/qtscxml-index.qdoc5
-rw-r--r--src/scxml/doc/qtscxml-instantiating-state-machines.qdoc54
-rw-r--r--src/scxml/doc/qtscxml-module-qml.qdoc4
-rw-r--r--src/scxml/doc/qtscxml-overview.qdoc20
-rw-r--r--src/scxml/doc/qtscxml-scxml-compliance.qdoc13
-rw-r--r--src/scxml/doc/qtscxml.qdocconf2
-rw-r--r--src/scxml/qscxmlcompiler.cpp (renamed from src/scxml/qscxmlparser.cpp)1616
-rw-r--r--src/scxml/qscxmlcompiler.h (renamed from src/scxml/qscxmlparser.h)49
-rw-r--r--src/scxml/qscxmlcompiler_p.h (renamed from src/scxml/qscxmlparser_p.h)83
-rw-r--r--src/scxml/qscxmlcppdatamodel.cpp4
-rw-r--r--src/scxml/qscxmlcppdatamodel.h4
-rw-r--r--src/scxml/qscxmldatamodel.h2
-rw-r--r--src/scxml/qscxmldatamodel_p.h2
-rw-r--r--src/scxml/qscxmlecmascriptdatamodel.cpp93
-rw-r--r--src/scxml/qscxmlecmascriptdatamodel.h5
-rw-r--r--src/scxml/qscxmlecmascriptplatformproperties.cpp2
-rw-r--r--src/scxml/qscxmlecmascriptplatformproperties_p.h2
-rw-r--r--src/scxml/qscxmlerror.cpp27
-rw-r--r--src/scxml/qscxmlerror.h10
-rw-r--r--src/scxml/qscxmlevent.cpp42
-rw-r--r--src/scxml/qscxmlevent.h36
-rw-r--r--src/scxml/qscxmlevent_p.h14
-rw-r--r--src/scxml/qscxmlexecutablecontent.cpp503
-rw-r--r--src/scxml/qscxmlexecutablecontent.h32
-rw-r--r--src/scxml/qscxmlexecutablecontent_p.h458
-rw-r--r--src/scxml/qscxmlglobals.h2
-rw-r--r--src/scxml/qscxmlinvokableservice.cpp264
-rw-r--r--src/scxml/qscxmlinvokableservice.h149
-rw-r--r--src/scxml/qscxmlinvokableservice_p.h (renamed from src/scxml/qscxmlqstates_p.h)69
-rw-r--r--src/scxml/qscxmlnulldatamodel.h2
-rw-r--r--src/scxml/qscxmlqstates.cpp410
-rw-r--r--src/scxml/qscxmlqstates.h206
-rw-r--r--src/scxml/qscxmlstatemachine.cpp2026
-rw-r--r--src/scxml/qscxmlstatemachine.h273
-rw-r--r--src/scxml/qscxmlstatemachine_p.h305
-rw-r--r--src/scxml/qscxmlstatemachineinfo.cpp249
-rw-r--r--src/scxml/qscxmlstatemachineinfo_p.h118
-rw-r--r--src/scxml/qscxmltabledata.cpp1000
-rw-r--r--src/scxml/qscxmltabledata.h13
-rw-r--r--src/scxml/qscxmltabledata_p.h127
-rw-r--r--src/scxml/scxml.pro19
-rw-r--r--tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.json4
-rw-r--r--tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.scxml28
-rw-r--r--tests/3rdparty/scion-tests/scxml-test-framework/test/test216sub1.scxml5
-rw-r--r--tests/auto/auto.pro4
-rw-r--r--tests/auto/cmake/CMakeLists.txt2
-rw-r--r--tests/auto/cmake/test_qtscxml_module/CMakeLists.txt16
-rw-r--r--tests/auto/cmake/test_qtscxml_module/main.cpp50
-rw-r--r--tests/auto/compiled/anonymousstate.scxml1
-rw-r--r--tests/auto/compiled/compiled.pro6
-rw-r--r--tests/auto/compiled/connection.scxml (renamed from tests/auto/parser/data/qtmode.scxml)24
-rw-r--r--tests/auto/compiled/eventnames1.scxml5
-rw-r--r--tests/auto/compiled/eventnames2.scxml1
-rw-r--r--tests/auto/compiled/submachineA.scxml4
-rw-r--r--tests/auto/compiled/submachineB.scxml4
-rw-r--r--tests/auto/compiled/submachineunicodename.scxml1
-rw-r--r--tests/auto/compiled/topmachine.scxml19
-rw-r--r--tests/auto/compiled/tst_compiled.cpp230
-rw-r--r--tests/auto/compiled/tst_compiled.qrc7
-rw-r--r--tests/auto/dynamicmetaobject/dynamicmetaobject.pro2
-rw-r--r--tests/auto/dynamicmetaobject/mediaplayer.scxml5
-rw-r--r--tests/auto/dynamicmetaobject/test1.scxml1
-rw-r--r--tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp46
-rw-r--r--tests/auto/parser/data/badInitial.scxml12
-rw-r--r--tests/auto/parser/data/badInitial.scxml.errors2
-rw-r--r--tests/auto/parser/data/commentInScript.scxml (renamed from tests/auto/qscxmlc/data/commentInScript.scxml)7
-rw-r--r--tests/auto/parser/data/commentInScript.scxml.errors1
-rw-r--r--tests/auto/parser/data/empty.scxml (renamed from tests/auto/qscxmlc/data/empty.scxml)0
-rw-r--r--tests/auto/parser/data/empty.scxml.errors1
-rw-r--r--tests/auto/parser/data/eventnames.scxml.errors5
-rw-r--r--tests/auto/parser/data/ids1.scxml.errors0
-rw-r--r--tests/auto/parser/data/ids2.scxml1
-rw-r--r--tests/auto/parser/data/ids2.scxml.errors1
-rw-r--r--tests/auto/parser/data/invalidContent.scxml16
-rw-r--r--tests/auto/parser/data/invalidContent.scxml.errors2
-rw-r--r--tests/auto/parser/data/invalidRoot1.scxml5
-rw-r--r--tests/auto/parser/data/invalidRoot1.scxml.errors2
-rw-r--r--tests/auto/parser/data/invalidRoot2.scxml17
-rw-r--r--tests/auto/parser/data/invalidRoot2.scxml.errors2
-rw-r--r--tests/auto/parser/data/invalidRoot3.scxml8
-rw-r--r--tests/auto/parser/data/invalidRoot3.scxml.errors2
-rw-r--r--tests/auto/parser/data/invalidRoot6.scxml18
-rw-r--r--tests/auto/parser/data/invalidRoot6.scxml.errors3
-rw-r--r--tests/auto/parser/data/invalidUnicode1.scxml16
-rw-r--r--tests/auto/parser/data/invalidUnicode1.scxml.errors1
-rw-r--r--tests/auto/parser/data/invalidXmlHeader1.scxmlbin0 -> 132 bytes
-rw-r--r--tests/auto/parser/data/invalidXmlHeader1.scxml.errors1
-rw-r--r--tests/auto/parser/data/invalidXmlHeader2.scxml5
-rw-r--r--tests/auto/parser/data/invalidXmlHeader2.scxml.errors1
-rw-r--r--tests/auto/parser/data/invalidstatemachinename.scxml (renamed from tests/auto/qscxmlc/data/invalidstatemachinename.scxml)0
-rw-r--r--tests/auto/parser/data/invalidstatemachinename.scxml.errors1
-rw-r--r--tests/auto/parser/data/misplacedinvoke.scxml (renamed from tests/auto/qscxmlc/data/misplacedinvoke.scxml)0
-rw-r--r--tests/auto/parser/data/misplacedinvoke.scxml.errors1
-rw-r--r--tests/auto/parser/data/namespaces1.scxml.errors0
-rw-r--r--tests/auto/parser/data/nestedScxml.scxml (renamed from tests/auto/qscxmlc/data/nestedScxml.scxml)0
-rw-r--r--tests/auto/parser/data/nestedScxml.scxml.errors1
-rw-r--r--tests/auto/parser/data/noContentInInvoke1.scxml15
-rw-r--r--tests/auto/parser/data/noContentInInvoke1.scxml.errors2
-rw-r--r--tests/auto/parser/data/noContentInInvoke2.scxml16
-rw-r--r--tests/auto/parser/data/noContentInInvoke2.scxml.errors1
-rw-r--r--tests/auto/parser/data/noContentInInvoke3.scxmlbin0 -> 507 bytes
-rw-r--r--tests/auto/parser/data/noContentInInvoke3.scxml.errors1
-rw-r--r--tests/auto/parser/data/noContentInInvoke4.scxml21
-rw-r--r--tests/auto/parser/data/noContentInInvoke4.scxml.errors1
-rw-r--r--tests/auto/parser/data/prematureEndOfDocument1.scxml9
-rw-r--r--tests/auto/parser/data/prematureEndOfDocument1.scxml.errors1
-rw-r--r--tests/auto/parser/data/prematureEndOfDocument2.scxml22
-rw-r--r--tests/auto/parser/data/prematureEndOfDocument2.scxml.errors1
-rw-r--r--tests/auto/parser/data/scxml1.scxml.errors2
-rw-r--r--tests/auto/parser/data/scxml2.scxml.errors1
-rw-r--r--tests/auto/parser/data/syntaxErrors1.scxml14
-rw-r--r--tests/auto/parser/data/syntaxErrors1.scxml.errors3
-rw-r--r--tests/auto/parser/data/syntaxErrors10.scxml12
-rw-r--r--tests/auto/parser/data/syntaxErrors10.scxml.errors2
-rw-r--r--tests/auto/parser/data/syntaxErrors11.scxml10
-rw-r--r--tests/auto/parser/data/syntaxErrors11.scxml.errors1
-rw-r--r--tests/auto/parser/data/syntaxErrors12.scxml16
-rw-r--r--tests/auto/parser/data/syntaxErrors12.scxml.errors7
-rw-r--r--tests/auto/parser/data/syntaxErrors13.scxml12
-rw-r--r--tests/auto/parser/data/syntaxErrors13.scxml.errors2
-rw-r--r--tests/auto/parser/data/syntaxErrors14.scxml10
-rw-r--r--tests/auto/parser/data/syntaxErrors14.scxml.errors2
-rw-r--r--tests/auto/parser/data/syntaxErrors15.scxml17
-rw-r--r--tests/auto/parser/data/syntaxErrors15.scxml.errors3
-rw-r--r--tests/auto/parser/data/syntaxErrors2.scxml14
-rw-r--r--tests/auto/parser/data/syntaxErrors2.scxml.errors3
-rw-r--r--tests/auto/parser/data/syntaxErrors3.scxml19
-rw-r--r--tests/auto/parser/data/syntaxErrors3.scxml.errors2
-rw-r--r--tests/auto/parser/data/syntaxErrors4.scxml17
-rw-r--r--tests/auto/parser/data/syntaxErrors4.scxml.errors2
-rw-r--r--tests/auto/parser/data/syntaxErrors5.scxml13
-rw-r--r--tests/auto/parser/data/syntaxErrors5.scxml.errors2
-rw-r--r--tests/auto/parser/data/syntaxErrors7.scxml12
-rw-r--r--tests/auto/parser/data/syntaxErrors7.scxml.errors1
-rw-r--r--tests/auto/parser/data/syntaxErrors9.scxml12
-rw-r--r--tests/auto/parser/data/syntaxErrors9.scxml.errors2
-rw-r--r--tests/auto/parser/data/test1.scxml.errors1
-rw-r--r--tests/auto/parser/parser.pro2
-rw-r--r--tests/auto/parser/tst_parser.cpp109
-rw-r--r--tests/auto/parser/tst_parser.qrc74
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf.scxml164
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf10.scxml374
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf2.scxml164
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf3.scxml163
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf4.scxml165
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf5.scxml374
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf6.scxml374
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf7.scxml374
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf8.scxml374
-rw-r--r--tests/auto/qscxmlc/data/elseWithoutIf9.scxml374
-rw-r--r--tests/auto/qscxmlc/data/invalidAssign.scxml164
-rw-r--r--tests/auto/qscxmlc/data/invalidAssign2.scxml164
-rw-r--r--tests/auto/qscxmlc/data/invalidIf.scxml164
-rw-r--r--tests/auto/qscxmlc/data/invalidIf2.scxml164
-rw-r--r--tests/auto/qscxmlc/data/invalidInvoke4.scxml79
-rw-r--r--tests/auto/qscxmlc/data/invalidRoot.scxml164
-rw-r--r--tests/auto/qscxmlc/data/invalidRoot3.scxmlbin3411 -> 0 bytes
-rw-r--r--tests/auto/qscxmlc/data/invalidRoot4.scxml374
-rw-r--r--tests/auto/qscxmlc/data/invalidScxml.scxml80
-rw-r--r--tests/auto/qscxmlc/data/invalidXmlHeader.scxmlbin7821 -> 0 bytes
-rw-r--r--tests/auto/qscxmlc/data/invalidXmlHeader2.scxml164
-rw-r--r--tests/auto/qscxmlc/data/noContentInInvoke4.scxml80
-rw-r--r--tests/auto/qscxmlc/data/prematureEndOfDocument.scxml80
-rw-r--r--tests/auto/qscxmlc/data/prematureEndOfDocument2.scxml80
-rw-r--r--tests/auto/qscxmlc/data/wrongRoot.scxml80
-rw-r--r--tests/auto/qscxmlc/qscxmlc.pro15
-rw-r--r--tests/auto/qscxmlc/tst_qscxmlc.qrc43
-rw-r--r--tests/auto/scion/scion.pro4
-rw-r--r--tests/auto/scion/tst_scion.cpp74
-rw-r--r--tests/auto/statemachine/eventoccurred.scxml18
-rw-r--r--tests/auto/statemachine/statemachine.pro4
-rw-r--r--tests/auto/statemachine/tst_statemachine.cpp235
-rw-r--r--tests/auto/statemachineinfo/statemachine.scxml (renamed from tests/auto/qscxmlc/tst_qscxmlc.cpp)53
-rw-r--r--tests/auto/statemachineinfo/statemachineinfo.pro13
-rw-r--r--tests/auto/statemachineinfo/tst_statemachineinfo.cpp237
-rw-r--r--tests/auto/statemachineinfo/tst_statemachineinfo.qrc5
-rw-r--r--tools/qscxmlc/cppdatamodel.t34
-rw-r--r--tools/qscxmlc/data.t135
-rw-r--r--tools/qscxmlc/decl.t16
-rw-r--r--tools/qscxmlc/doc/qscxmlc.qdoc6
-rw-r--r--tools/qscxmlc/generator.cpp14
-rw-r--r--tools/qscxmlc/generator.h6
-rw-r--r--tools/qscxmlc/main.cpp1
-rw-r--r--tools/qscxmlc/moc.h11
-rw-r--r--tools/qscxmlc/qscxmlc.cpp82
-rw-r--r--tools/qscxmlc/qscxmlc.pri6
-rw-r--r--tools/qscxmlc/qscxmlc.pro3
-rw-r--r--tools/qscxmlc/scxmlcppdumper.cpp1891
-rw-r--r--tools/qscxmlc/scxmlcppdumper.h35
-rw-r--r--tools/qscxmlc/templates.qrc7
293 files changed, 9798 insertions, 10808 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6b2abde
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+.tag export-subst
+.gitattributes export-ignore
+.gitignore export-ignore
diff --git a/.qmake.conf b/.qmake.conf
index 49570b2..0957f58 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -2,4 +2,4 @@ load(qt_build_config)
CONFIG += qt_example_installs
CONFIG += warning_clean
-MODULE_VERSION = 5.7.1
+MODULE_VERSION = 5.8.0
diff --git a/.tag b/.tag
new file mode 100644
index 0000000..6828f88
--- /dev/null
+++ b/.tag
@@ -0,0 +1 @@
+$Format:%H$
diff --git a/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc b/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc
index 54fe26b..3e5982f 100644
--- a/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc
+++ b/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc
@@ -9,11 +9,8 @@
We then specify the state machine to compile:
\skipto STATECHARTS
- \printline calculator
+ \printuntil statemachine.scxml
- We also tell qmake to run \c qscxmlc, which generates \e statemachine.h and
- \e statemachine.cpp, and adds them to the \c HEADERS and \c SOURCES
- variables for compilation:
-
- \skipto load
- \printline qscxmlc
+ The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
+ \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
+ and \c SOURCES variables for compilation.
diff --git a/examples/scxml/calculator-common/statemachine.scxml b/examples/scxml/calculator-common/statemachine.scxml
index 7971ee0..b1c6bb7 100644
--- a/examples/scxml/calculator-common/statemachine.scxml
+++ b/examples/scxml/calculator-common/statemachine.scxml
@@ -144,7 +144,7 @@
</transition>
<transition event="DISPLAY.UPDATE">
<log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
+ <send event="updateDisplay">
<param name="display" expr="short_expr==''?res:short_expr"/>
</send>
</transition>
diff --git a/examples/scxml/calculator-qml/calculator-qml.pro b/examples/scxml/calculator-qml/calculator-qml.pro
index 6221576..c40242f 100644
--- a/examples/scxml/calculator-qml/calculator-qml.pro
+++ b/examples/scxml/calculator-qml/calculator-qml.pro
@@ -11,5 +11,3 @@ STATECHARTS = ../calculator-common/statemachine.scxml
# install
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/calculator-qml
INSTALLS += target
-
-load(qscxmlc)
diff --git a/examples/scxml/calculator-qml/calculator-qml.qml b/examples/scxml/calculator-qml/calculator-qml.qml
index 846589f..386549e 100644
--- a/examples/scxml/calculator-qml/calculator-qml.qml
+++ b/examples/scxml/calculator-qml/calculator-qml.qml
@@ -51,6 +51,7 @@
import CalculatorStateMachine 1.0
import QtQuick 2.5
import QtQuick.Window 2.0
+import QtScxml 5.8
Window {
id: window
@@ -61,9 +62,9 @@ Window {
CalculatorStateMachine {
id: statemachine
running: true
- onEventOccurred: {
- if (event.name === "updateDisplay")
- resultText.text = event.data.display;
+ EventConnection {
+ events: ["updateDisplay"]
+ onOccurred: resultText.text = event.data.display
}
}
diff --git a/examples/scxml/calculator-widgets/calculator-widgets.pro b/examples/scxml/calculator-widgets/calculator-widgets.pro
index 55880f1..238980b 100644
--- a/examples/scxml/calculator-widgets/calculator-widgets.pro
+++ b/examples/scxml/calculator-widgets/calculator-widgets.pro
@@ -17,5 +17,3 @@ FORMS += \
# install
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/calculator-widgets
INSTALLS += target
-
-load(qscxmlc)
diff --git a/examples/scxml/calculator-widgets/doc/src/calculator.qdoc b/examples/scxml/calculator-widgets/doc/src/calculator.qdoc
index 5055005..9463b53 100644
--- a/examples/scxml/calculator-widgets/doc/src/calculator.qdoc
+++ b/examples/scxml/calculator-widgets/doc/src/calculator.qdoc
@@ -66,9 +66,16 @@
\printuntil digit2
\printuntil }
- To be notified when a state machine sends out an event, we connect to the
- corresponding signal:
+ The state machine can notify other code when events occur:
- \skipto eventOccurred
+ \quotefromfile calculator-common/statemachine.scxml
+ \skipto transition event="DISPLAY.UPDATE
+ \printuntil </transition
+
+ We connect to the \c updateDisplay event to display the data passed by
+ the events:
+
+ \quotefromfile calculator-widgets/mainwindow.cpp
+ \skipto connectToEvent
\printuntil });
*/
diff --git a/examples/scxml/calculator-widgets/mainwindow.cpp b/examples/scxml/calculator-widgets/mainwindow.cpp
index eacafaa..d8c77f7 100644
--- a/examples/scxml/calculator-widgets/mainwindow.cpp
+++ b/examples/scxml/calculator-widgets/mainwindow.cpp
@@ -115,11 +115,9 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
m_machine->submitEvent("C");
});
- connect(m_machine, &QScxmlStateMachine::eventOccurred, [this](const QScxmlEvent &event) {
- if (event.name() == QLatin1String("updateDisplay")) {
- const QString display = event.data().toMap().value("display").toString();
- ui->display->setText(display);
- }
+ m_machine->connectToEvent(QLatin1String("updateDisplay"), this, [this](const QScxmlEvent &event) {
+ const QString display = event.data().toMap().value("display").toString();
+ ui->display->setText(display);
});
}
diff --git a/examples/scxml/ftpclient/doc/images/ftpclient-statechart.png b/examples/scxml/ftpclient/doc/images/ftpclient-statechart.png
new file mode 100644
index 0000000..48decb2
--- /dev/null
+++ b/examples/scxml/ftpclient/doc/images/ftpclient-statechart.png
Binary files differ
diff --git a/examples/scxml/ftpclient/doc/src/ftpclient.qdoc b/examples/scxml/ftpclient/doc/src/ftpclient.qdoc
new file mode 100644
index 0000000..b69994d
--- /dev/null
+++ b/examples/scxml/ftpclient/doc/src/ftpclient.qdoc
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example ftpclient
+ \title Qt SCXML FTP Client Example
+ \ingroup examples-qtscxml
+
+ \brief Implements a simple FTP client using a state machine.
+
+ \e {FTP Client} uses Qt SCXML to implement a FTP client that can communicate
+ with a FTP service by sending FTP control messages translated from state
+ machine events and by translating server replies into state machine
+ events. The data received from the FTP server is printed on the console.
+
+ \l{RFC 959} specifies state charts for the command handling of the FTP
+ client. They can be easily translated into SCXML to benefit from SCXML
+ nested states. Connections between the client and server and data transfer
+ are implemented by using C++. In addition, Qt signals and slots are used.
+
+ The state machine has the following states:
+
+ \image ftpclient-statechart.png
+
+ \list
+ \li \e I as the initial state.
+ \li \e B for sending commands.
+ \li \e S for success.
+ \li \e F for failure.
+ \li \e W for waiting for a reply.
+ \li \e P for supplying a password upon server request.
+ \endlist
+
+ The state machine is specified in the \e simpleftp.scxml file and compiled
+ into the \c FtpClient class that implements the logic of the FTP protocol.
+ It reacts to user input and to replies from the control channel by changing
+ states and sending external events. In addition, we implement a
+ \c FtpControlChannel class and a \c FtpDataChannel class that handle TCP
+ sockets and servers and convert line endings.
+
+ \include examples-run.qdocinc
+
+ \section1 Compiling the State Machine
+
+ We link against the Qt SCXML module by adding the following line to the
+ \e .pro file:
+
+ \quotefromfile ftpclient/ftpclient.pro
+ \printuntil scxml
+
+ We then specify the state machine to compile:
+
+ \skipto STATECHARTS
+ \printuntil simpleftp.scxml
+
+ The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
+ \e ftpclient.h and \e ftpclient.cpp, and to add them to the \c HEADERS
+ and \c SOURCES variables for compilation.
+
+ \section1 Instantiating the State Machine
+
+ We instantiate the generated \c FtpClient class, as well as the
+ \c FtpDataChannel and \c FtpControlChannel classes in the \e main.cpp file:
+
+ \quotefromfile ftpclient/main.cpp
+ \skipto #include
+ \printuntil ftpdatachannel.h
+ \dots
+ \skipto int main
+ \printuntil {
+ \dots
+ \skipto QCoreApplication
+ \printuntil FtpControlChannel
+ \dots
+
+ \section1 Communicating with an FTP Server
+
+ We print all data retrieved from the server on the console:
+
+ \skipto QObject::connect(&dataChannel
+ \printuntil }
+
+ We translate server replies into state machine events:
+
+ \skipto QObject::connect(&controlChannel
+ \printuntil }
+
+ We translate commands from the state machine into FTP control messages:
+
+ \skipto ftpClient.connectToEvent(
+ \printuntil }
+
+ We send commands to log into the FTP server as an anonymous user, to
+ announce a port for the data connection, and to retrive a file:
+
+ \skipto QList
+ \printuntil });
+
+ We specify that the FTP client should send the next command when entering
+ the \e B state:
+
+ \skipto ftpClient.connectToState("B"
+ \printuntil }));
+
+ We specify that the FTP client should send an empty string as a password if
+ the server asks for one:
+
+ \skipto ftpClient.connectToState("P"
+ \printuntil }
+
+ Finally, we connect to the FTP server specified as the first argument of
+ the method and retrieve the file specified as the second argument:
+
+ \skipto controlChannel.connectToServer(server)
+ \printuntil }
+
+ For example, the following invocation prints the specified file from the
+ specified server: \c {ftpclient <server> <file>}.
+*/
diff --git a/examples/scxml/ftpclient/ftpclient.pro b/examples/scxml/ftpclient/ftpclient.pro
new file mode 100644
index 0000000..3ba8adf
--- /dev/null
+++ b/examples/scxml/ftpclient/ftpclient.pro
@@ -0,0 +1,18 @@
+QT = core scxml
+
+TARGET = ftpclient
+
+TEMPLATE = app
+STATECHARTS += simpleftp.scxml
+
+SOURCES += \
+ main.cpp \
+ ftpcontrolchannel.cpp \
+ ftpdatachannel.cpp
+
+HEADERS += \
+ ftpcontrolchannel.h \
+ ftpdatachannel.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/scxml/ftpclient
+INSTALLS += target
diff --git a/tests/auto/qscxmlc/data/invalidInvoke2.scxml b/examples/scxml/ftpclient/ftpcontrolchannel.cpp
index 218474e..c37b6e5 100644
--- a/tests/auto/qscxmlc/data/invalidInvoke2.scxml
+++ b/examples/scxml/ftpclient/ftpcontrolchannel.cpp
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,31 +47,47 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/"> <content>
- <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
+#include "ftpcontrolchannel.h"
+
+FtpControlChannel::FtpControlChannel(QObject *parent) : QObject(parent)
+{
+ connect(&m_socket, &QIODevice::readyRead, this, &FtpControlChannel::onReadyRead);
+ connect(&m_socket, &QAbstractSocket::disconnected, this, &FtpControlChannel::closed);
+ connect(&m_socket, &QAbstractSocket::connected, this, [this]() {
+ emit opened(m_socket.localAddress(), m_socket.localPort());
+ });
+}
+
+void FtpControlChannel::connectToServer(const QString &server)
+{
+ m_socket.connectToHost(server, 21);
+}
+
+void FtpControlChannel::command(const QByteArray &command, const QByteArray &params)
+{
+ QByteArray sendData = command;
+ if (!params.isEmpty())
+ sendData += " " + params;
+ m_socket.write(sendData + "\r\n");
+}
+
+void FtpControlChannel::onReadyRead()
+{
+ m_buffer.append(m_socket.readAll());
+ int rn = -1;
+ while ((rn = m_buffer.indexOf("\r\n")) != -1) {
+ QByteArray received = m_buffer.mid(0, rn);
+ m_buffer = m_buffer.mid(rn + 2);
+ int space = received.indexOf(' ');
+ if (space != -1) {
+ int code = received.mid(0, space).toInt();
+ if (code == 0)
+ emit info(received.mid(space + 1));
+ else
+ emit reply(code, received.mid(space + 1));
+ } else {
+ emit invalidReply(received);
+ }
+ }
+}
diff --git a/tests/auto/qscxmlc/data/invalidInvoke3.scxml b/examples/scxml/ftpclient/ftpcontrolchannel.h
index 10e9130..ce8ad72 100644
--- a/tests/auto/qscxmlc/data/invalidInvoke3.scxml
+++ b/examples/scxml/ftpclient/ftpcontrolchannel.h
Binary files differ
diff --git a/tests/auto/qscxmlc/data/invalidInvoke.scxml b/examples/scxml/ftpclient/ftpdatachannel.cpp
index 5758c04..f0a7aa4 100644
--- a/tests/auto/qscxmlc/data/invalidInvoke.scxml
+++ b/examples/scxml/ftpclient/ftpdatachannel.cpp
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,31 +47,47 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/"> <content>
- <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
+#include "ftpdatachannel.h"
+
+FtpDataChannel::FtpDataChannel(QObject *parent) : QObject(parent)
+{
+ connect(&m_server, &QTcpServer::newConnection, this, [this]() {
+ m_socket.reset(m_server.nextPendingConnection());
+ connect(m_socket.data(), &QTcpSocket::readyRead, [this]() {
+ emit dataReceived(m_socket->readAll());
+ });
+ });
+}
+
+void FtpDataChannel::listen(const QHostAddress &address)
+{
+ m_server.listen(address);
+}
+
+void FtpDataChannel::sendData(const QByteArray &data)
+{
+ if (m_socket)
+ m_socket->write(QByteArray(data).replace("\n", "\r\n"));
+}
+
+void FtpDataChannel::close()
+{
+ if (m_socket)
+ m_socket->disconnectFromHost();
+}
+
+QString FtpDataChannel::portspec() const
+{
+ // Yes, this is a weird format, but say hello to FTP.
+ QString portSpec;
+ quint32 ipv4 = m_server.serverAddress().toIPv4Address();
+ quint16 port = m_server.serverPort();
+ portSpec += QString::number((ipv4 & 0xff000000) >> 24);
+ portSpec += ',' + QString::number((ipv4 & 0x00ff0000) >> 16);
+ portSpec += ',' + QString::number((ipv4 & 0x0000ff00) >> 8);
+ portSpec += ',' + QString::number(ipv4 & 0x000000ff);
+ portSpec += ',' + QString::number((port & 0xff00) >> 8);
+ portSpec += ',' + QString::number(port &0x00ff);
+ return portSpec;
+}
diff --git a/tests/auto/qscxmlc/data/invalidContent.scxml b/examples/scxml/ftpclient/ftpdatachannel.h
index 387bf21..2ca211c 100644
--- a/tests/auto/qscxmlc/data/invalidContent.scxml
+++ b/examples/scxml/ftpclient/ftpdatachannel.h
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,31 +47,45 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content /> <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
+#ifndef FTPDATACHANNEL_H
+#define FTPDATACHANNEL_H
+
+#include <QObject>
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QScopedPointer>
+
+class FtpDataChannel : public QObject
+{
+ Q_OBJECT
+public:
+ explicit FtpDataChannel(QObject *parent = 0);
+
+ // Listen on a local address.
+ void listen(const QHostAddress &address = QHostAddress::Any);
+
+ // Send data over the socket.
+ void sendData(const QByteArray &data);
+
+ // Close the connection.
+ void close();
+
+ // Retrieve the port specification to be announced on the control channel.
+ // Something like "a,b,c,d,xxx,yyy" where
+ // - a.b.c.d would be the IP address in decimal/dot notation and
+ // - xxx,yyy are the upper and lower 8 bits of the TCP port in decimal
+ // (This will only work if the local address we're listening on is actually meaningful)
+ QString portspec() const;
+
+signals:
+
+ // The FTP server has sent some data.
+ void dataReceived(const QByteArray &data);
+
+private:
+ QTcpServer m_server;
+ QScopedPointer<QTcpSocket> m_socket;
+};
+
+#endif // FTPDATACHANNEL_H
diff --git a/examples/scxml/ftpclient/main.cpp b/examples/scxml/ftpclient/main.cpp
new file mode 100644
index 0000000..25102d2
--- /dev/null
+++ b/examples/scxml/ftpclient/main.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "simpleftp.h"
+#include "ftpcontrolchannel.h"
+#include "ftpdatachannel.h"
+#include <QCoreApplication>
+#include <iostream>
+
+struct Command {
+ QString cmd;
+ QString args;
+};
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3) {
+ qDebug() << "Usage: ftpclient <server> <file>";
+ return 1;
+ }
+
+ QString server = QString::fromLocal8Bit(argv[1]);
+ QString file = QString::fromLocal8Bit(argv[2]);
+
+ QCoreApplication app(argc, argv);
+ FtpClient ftpClient;
+ FtpDataChannel dataChannel;
+ FtpControlChannel controlChannel;
+
+ // Print all data retrieved from the server on the console.
+ QObject::connect(&dataChannel, &FtpDataChannel::dataReceived, [](const QByteArray &data) {
+ std::cout << data.constData();
+ });
+
+ // Translate server replies into state machine events.
+ QObject::connect(&controlChannel, &FtpControlChannel::reply, &ftpClient,
+ [&ftpClient](int code, const QString &parameters) {
+ ftpClient.submitEvent(QString("reply.%1xx").arg(code / 100), parameters);
+ });
+
+ // Translate commands from the state machine into FTP control messages.
+ ftpClient.connectToEvent("submit.cmd", &controlChannel,
+ [&controlChannel](const QScxmlEvent &event) {
+ controlChannel.command(event.name().mid(11).toUtf8(), event.data().toByteArray());
+ });
+
+ // Commands to be sent
+ QList<Command> commands({
+ {"cmd.USER", "anonymous"},// login
+ {"cmd.PORT", ""}, // announce port for data connection, args added below.
+ {"cmd.RETR", file} // retrieve a file
+ });
+
+ // When entering "B" state, send the next command.
+ ftpClient.connectToState("B", QScxmlStateMachine::onEntry([&]() {
+ if (commands.isEmpty()) {
+ app.quit();
+ return;
+ }
+ Command command = commands.takeFirst();
+ qDebug() << "Posting command" << command.cmd << command.args;
+ ftpClient.submitEvent(command.cmd, command.args);
+ }));
+
+ // If the server asks for a password, send one.
+ ftpClient.connectToState("P", QScxmlStateMachine::onEntry([&ftpClient]() {
+ qDebug() << "Sending password";
+ ftpClient.submitEvent("cmd.PASS", QString());
+ }));
+
+ // Connect to our own local FTP server
+ controlChannel.connectToServer(server);
+ QObject::connect(&controlChannel, &FtpControlChannel::opened,
+ [&](const QHostAddress &address, int) {
+ dataChannel.listen(address);
+ commands[1].args = dataChannel.portspec();
+ ftpClient.start();
+ });
+
+ return app.exec();
+}
diff --git a/examples/scxml/ftpclient/simpleftp.scxml b/examples/scxml/ftpclient/simpleftp.scxml
new file mode 100644
index 0000000..79eb7b9
--- /dev/null
+++ b/examples/scxml/ftpclient/simpleftp.scxml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="FtpClient"
+ datamodel="ecmascript">
+ <state id="G" initial="I">
+ <transition event="reply" target="E"/>
+ <transition event="cmd" target="F"/>
+
+ <state id="I">
+ <transition event="reply.2xx" target="S"/>
+ </state>
+
+ <state id="B">
+ <transition event="cmd.DELE cmd.CWD cmd.CDUP cmd.HELP cmd.NOOP cmd.QUIT cmd.SYST
+ cmd.STAT cmd.RMD cmd.MKD cmd.PWD cmd.PORT"
+ target="W.general"/>
+ <transition event="cmd.APPE cmd.LIST cmd.NLST cmd.REIN cmd.RETR cmd.STOR cmd.STOU"
+ target="W.1xx"/>
+ <transition event="cmd.USER" target="W.user"/>
+
+ <state id="S"/>
+ <state id="F"/>
+ </state>
+
+ <state id="W">
+ <onentry>
+ <send eventexpr="&quot;submit.&quot; + _event.name">
+ <content expr="_event.data"/>
+ </send>
+ </onentry>
+
+ <transition event="reply.2xx" target="S"/>
+ <transition event="reply.4xx reply.5xx" target="F"/>
+
+ <state id="W.1xx">
+ <transition event="reply.1xx" target="W.transfer"/>
+ </state>
+ <state id="W.transfer"/>
+ <state id="W.general"/>
+ <state id="W.user">
+ <transition event="reply.3xx" target="P"/>
+ </state>
+ <state id="W.login"/>
+ </state>
+
+ <state id="P">
+ <transition event="cmd.PASS" target="W.login"/>
+ </state>
+ </state>
+
+ <final id="E"/>
+</scxml>
diff --git a/examples/scxml/invoke-common/MainView.qml b/examples/scxml/invoke-common/MainView.qml
index 0f51ae5..8d8c3e3 100644
--- a/examples/scxml/invoke-common/MainView.qml
+++ b/examples/scxml/invoke-common/MainView.qml
@@ -50,11 +50,12 @@
import QtQuick 2.5
import QtQuick.Window 2.2
+import QtScxml 5.8
Window {
id: window
visible: true
- property var stateMachine
+ property StateMachine stateMachine
color: "black"
width: 400
@@ -69,7 +70,7 @@ Window {
text: "Go Nowhere"
width: parent.width
height: parent.height / 2
- onClicked: stateMachine.goNowhere()
+ onClicked: stateMachine.submitEvent("goNowhere")
enabled: stateMachine.somewhere
}
@@ -79,7 +80,7 @@ Window {
width: parent.width
height: parent.height / 2
y: parent.height / 2
- onClicked: stateMachine.goSomewhere()
+ onClicked: stateMachine.submitEvent("goSomewhere")
enabled: stateMachine.nowhere
}
}
@@ -91,7 +92,14 @@ Window {
x: parent.width / 2
width: parent.width / 2
height: parent.height
- property var anywhere: stateMachine.anywhere
+
+ InvokedServices {
+ id: services
+ stateMachine: window.stateMachine
+ }
+
+ property var anywhere: services.children.anywhere ? services.children.anywhere.stateMachine
+ : undefined
}
}
diff --git a/examples/scxml/invoke-common/SubView.qml b/examples/scxml/invoke-common/SubView.qml
index 61cc723..2c374d5 100644
--- a/examples/scxml/invoke-common/SubView.qml
+++ b/examples/scxml/invoke-common/SubView.qml
@@ -58,7 +58,7 @@ Item {
text: "Go There"
width: parent.width / 2
height: parent.height
- onClicked: anywhere.goThere()
+ onClicked: anywhere.submitEvent("goThere")
}
Button {
@@ -68,6 +68,6 @@ Item {
width: parent.width / 2
height: parent.height
x: width
- onClicked: anywhere.goHere()
+ onClicked: anywhere.submitEvent("goHere")
}
}
diff --git a/examples/scxml/invoke-common/statemachine.scxml b/examples/scxml/invoke-common/statemachine.scxml
index b9defeb..ad04cb3 100644
--- a/examples/scxml/invoke-common/statemachine.scxml
+++ b/examples/scxml/invoke-common/statemachine.scxml
@@ -50,7 +50,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
diff --git a/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc b/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc
index 8c0f570..bff773a 100644
--- a/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc
+++ b/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc
@@ -47,7 +47,7 @@
\e Directions of type \e http://www.w3.org/TR/scxml/ to invoke:
\quotefromfile invoke-common/statemachine.scxml
- \skipto enable-qt-mode
+ \skipto scxml
\printuntil
\section1 Dynamically Loading the State Machine
diff --git a/examples/scxml/invoke-dynamic/invoke-dynamic.qml b/examples/scxml/invoke-dynamic/invoke-dynamic.qml
index 616928e..4ff85b4 100644
--- a/examples/scxml/invoke-dynamic/invoke-dynamic.qml
+++ b/examples/scxml/invoke-dynamic/invoke-dynamic.qml
@@ -48,13 +48,13 @@
**
****************************************************************************/
-import QtScxml 5.7 as Scxml
+import QtScxml 5.8
MainView {
stateMachine: directions.stateMachine
- Scxml.StateMachineLoader {
+ StateMachineLoader {
id: directions
- filename: "qrc:///statemachine.scxml"
+ source: "qrc:///statemachine.scxml"
}
}
diff --git a/examples/scxml/invoke-static/doc/src/invoke-static.qdoc b/examples/scxml/invoke-static/doc/src/invoke-static.qdoc
index d2ecc78..099b904 100644
--- a/examples/scxml/invoke-static/doc/src/invoke-static.qdoc
+++ b/examples/scxml/invoke-static/doc/src/invoke-static.qdoc
@@ -47,7 +47,7 @@
\e Directions of type \e http://www.w3.org/TR/scxml/ to invoke:
\quotefromfile invoke-common/statemachine.scxml
- \skipto enable-qt-mode
+ \skipto scxml
\printuntil
\section1 Compiling the State Machine
@@ -64,11 +64,9 @@
\skipto STATECHARTS
\printline statemachine
- We also tell qmake to run \c qscxmlc, which generates \e statemachine.h and
- \e statemachine.cpp, and adds them to the \c HEADERS and \c SOURCES
- variables for compilation:
-
- \printuntil qscxmlc
+ The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
+ \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
+ and \c SOURCES variables for compilation.
\section1 Instantiating the State Machine
diff --git a/examples/scxml/invoke-static/invoke-static.pro b/examples/scxml/invoke-static/invoke-static.pro
index 86e0845..e2e206c 100644
--- a/examples/scxml/invoke-static/invoke-static.pro
+++ b/examples/scxml/invoke-static/invoke-static.pro
@@ -9,8 +9,6 @@ RESOURCES += invoke-static.qrc
STATECHARTS = ../invoke-common/statemachine.scxml
-load(qscxmlc)
-
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/invoke-static
INSTALLS += target
diff --git a/examples/scxml/mediaplayer-common/Mediaplayer.qml b/examples/scxml/mediaplayer-common/Mediaplayer.qml
index 4d793fa..fdeb9d2 100644
--- a/examples/scxml/mediaplayer-common/Mediaplayer.qml
+++ b/examples/scxml/mediaplayer-common/Mediaplayer.qml
@@ -50,12 +50,12 @@
import QtQuick 2.5
import QtQuick.Window 2.2
-import QtScxml 5.7 as Scxml
+import QtScxml 5.8
Window {
id: root
- property QtObject stateMachine: scxmlLoader.stateMachine
- property alias filename: scxmlLoader.filename
+ property StateMachine stateMachine: scxmlLoader.stateMachine
+ property alias source: scxmlLoader.source
visible: true
width: 750
@@ -111,27 +111,30 @@ Window {
color: stateMachine.playing ? "green" : "red"
}
- Scxml.StateMachineLoader {
+ StateMachineLoader {
id: scxmlLoader
}
- Connections {
- target: stateMachine
- onPlaybackStarted: {
- var media = data.media
- theText.text = "Playing '" + media + "'"
- theLog.text = theLog.text + "\nplaybackStarted with data: " + JSON.stringify(data)
- }
- onPlaybackStopped: {
- var media = data.media
- theText.text = "Stopped '" + media + "'"
- theLog.text = theLog.text + "\nplaybackStopped with data: " + JSON.stringify(data)
+ EventConnection {
+ stateMachine: root.stateMachine
+ events: ["playbackStarted", "playbackStopped"]
+ onOccurred: {
+ var media = event.data.media
+ if (event.name === "playbackStarted") {
+ theText.text = "Playing '" + media + "'"
+ theLog.text = theLog.text + "\nplaybackStarted with data: "
+ + JSON.stringify(event.data)
+ } else if (event.name === "playbackStopped") {
+ theText.text = "Stopped '" + media + "'"
+ theLog.text = theLog.text + "\nplaybackStopped with data: "
+ + JSON.stringify(event.data)
+ }
}
}
function tap(idx) {
var media = theModel.get(idx).media
var data = { "media": media }
- stateMachine.tap(data)
+ stateMachine.submitEvent("tap", data)
}
}
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc
index d4eb6ee..711a32f 100644
--- a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc
+++ b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc
@@ -12,9 +12,6 @@
\skipto STATECHARTS
\printline scxml
- We also tell qmake to run \c qscxmlc, which generates \e mediaplayer.h and
- \e mediaplayer.cpp, and adds them to the \c HEADERS and \c SOURCES
- variables for compilation:
-
- \skipto load
- \printline qscxmlc
+ The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
+ \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
+ and \c SOURCES variables for compilation.
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-dynamic.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-dynamic.qdocinc
deleted file mode 100644
index 94062ad..0000000
--- a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-dynamic.qdocinc
+++ /dev/null
@@ -1,15 +0,0 @@
- \section1 Dynamically Loading the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- example \e .pro file:
-
- \quotefromfile mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
- \skipto QT
- \printline scxml
-
- We dynamically create the state machine in
- \e mediaplayer-common\Mediaplayer.qml:
-
- \quotefromfile mediaplayer-common/Mediaplayer.qml
- \skipto import QtScxml
- \printuntil scxmlLoader.filename
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc
index 31218ef..98b33c7 100644
--- a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc
+++ b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc
@@ -4,6 +4,6 @@
\c <scxml> element in \e mediaplayer-common/mediaplayer.scxml:
\quotefromfile mediaplayer-common/mediaplayer.scxml
- \skipto enable-qt-mode
+ \skipto scxml
\printuntil </datamodel>
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc
new file mode 100644
index 0000000..207f47d
--- /dev/null
+++ b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc
@@ -0,0 +1,16 @@
+ \section1 Connecting to States
+
+ The media player state machine will send out events when users tap a control
+ and when playback starts or stops, as specified in the SCXML file:
+
+ \quotefromfile mediaplayer-common/mediaplayer.scxml
+ \skipto <state
+ \printuntil !==
+ \printuntil </state
+
+ To be notified when a state machine sends out an event, we connect to the
+ corresponding signals:
+
+ \quotefromfile mediaplayer-common/mainwindow.cpp
+ \skipto connectToEvent
+ \printuntil playbackStopped
diff --git a/examples/scxml/mediaplayer-common/mainwindow.cpp b/examples/scxml/mediaplayer-common/mainwindow.cpp
index e9a5767..b4d3760 100644
--- a/examples/scxml/mediaplayer-common/mainwindow.cpp
+++ b/examples/scxml/mediaplayer-common/mainwindow.cpp
@@ -52,10 +52,11 @@
#include "ui_mainwindow.h"
#include <QStringListModel>
+#include <QScxmlStateMachine>
QT_USE_NAMESPACE
-MainWindow::MainWindow(QWidget *parent) :
+MainWindow::MainWindow(QScxmlStateMachine *stateMachine, QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWindow)
{
@@ -66,11 +67,15 @@ MainWindow::MainWindow(QWidget *parent) :
<< QStringLiteral("song 3"), this);
ui->mediaListView->setModel(model);
- connect(ui->mediaListView, &QAbstractItemView::clicked, [model,this](const QModelIndex & index){
+ connect(ui->mediaListView, &QAbstractItemView::clicked,
+ [model, stateMachine](const QModelIndex & index) {
QVariantMap data;
data.insert(QStringLiteral("media"), model->data(index, Qt::EditRole).toString());
- emit tap(data);
+ stateMachine->submitEvent("tap", data);
});
+
+ stateMachine->connectToEvent("playbackStarted", this, &MainWindow::started);
+ stateMachine->connectToEvent("playbackStopped", this, &MainWindow::stopped);
}
MainWindow::~MainWindow()
@@ -78,16 +83,16 @@ MainWindow::~MainWindow()
delete ui;
}
-void MainWindow::started(const QVariant &data)
+void MainWindow::started(const QScxmlEvent &event)
{
- QString media = data.toMap().value("media").toString();
+ const QString media = event.data().toMap().value("media").toString();
ui->logText->appendPlainText(QStringLiteral("call on slot started with media '%1'").arg(media));
ui->statusLabel->setText(QStringLiteral("Playing %1").arg(media));
}
-void MainWindow::stopped(const QVariant &data)
+void MainWindow::stopped(const QScxmlEvent &event)
{
- QString media = data.toMap().value("media").toString();
+ const QString media = event.data().toMap().value("media").toString();
ui->logText->appendPlainText(QStringLiteral("call on slot stopped with media '%1'").arg(media));
ui->statusLabel->setText(QStringLiteral("Stopped"));
}
diff --git a/examples/scxml/mediaplayer-common/mainwindow.h b/examples/scxml/mediaplayer-common/mainwindow.h
index e9ed5fe..c89c269 100644
--- a/examples/scxml/mediaplayer-common/mainwindow.h
+++ b/examples/scxml/mediaplayer-common/mainwindow.h
@@ -57,6 +57,10 @@ QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
+
+class QScxmlEvent;
+class QScxmlStateMachine;
+
QT_END_NAMESPACE
class MainWindow : public QWidget
@@ -64,15 +68,12 @@ class MainWindow : public QWidget
Q_OBJECT
public:
- explicit MainWindow(QWidget *parent = 0);
+ explicit MainWindow(QScxmlStateMachine *stateMachine, QWidget *parent = 0);
~MainWindow();
-signals:
- void tap(const QVariant &data);
-
-public slots:
- void started(const QVariant &data);
- void stopped(const QVariant &data);
+private slots:
+ void started(const QScxmlEvent &event);
+ void stopped(const QScxmlEvent &event);
private:
QT_PREPEND_NAMESPACE(Ui::MainWindow) *ui;
diff --git a/examples/scxml/mediaplayer-common/mediaplayer.scxml b/examples/scxml/mediaplayer-common/mediaplayer.scxml
index 025245a..ca68039 100644
--- a/examples/scxml/mediaplayer-common/mediaplayer.scxml
+++ b/examples/scxml/mediaplayer-common/mediaplayer.scxml
@@ -50,7 +50,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
@@ -76,13 +75,13 @@
<state id="playing">
<onentry>
<assign location="media" expr="_event.data.media"/>
- <send type="qt:signal" event="playbackStarted">
+ <send event="playbackStarted">
<param name="media" expr="media"/>
</send>
</onentry>
<onexit>
- <send type="qt:signal" event="playbackStopped">
+ <send event="playbackStopped">
<param name="media" expr="media"/>
</send>
</onexit>
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc b/examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc
index 633922a..421e72e 100644
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc
+++ b/examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc
@@ -50,7 +50,7 @@
\c <scxml> element in the SCXML file:
\quotefromfile mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
- \skipto enable-qt-mode
+ \skipto scxml
\printuntil datamodel
The format of the \e datamodel attribute is:
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
index 3683c19..4ca810e 100644
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
+++ b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
@@ -50,7 +50,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
@@ -67,13 +66,13 @@
<script>
media = eventData().value(QStringLiteral(&quot;media&quot;)).toString();
</script>
- <send type="qt:signal" event="playbackStarted">
+ <send event="playbackStarted">
<param name="media" expr="media"/>
</send>
</onentry>
<onexit>
- <send type="qt:signal" event="playbackStopped">
+ <send event="playbackStopped">
<param name="media" expr="media"/>
</send>
</onexit>
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro
index fae454d..2d21a37 100644
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro
+++ b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro
@@ -12,8 +12,6 @@ RESOURCES += mediaplayer-qml-cppdatamodel.qrc
STATECHARTS = mediaplayer-cppdatamodel.scxml
-load(qscxmlc)
-
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-qml-cppdatamodel
INSTALLS += target
diff --git a/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc b/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc
index 525974a..22d3ea2 100644
--- a/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc
+++ b/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc
@@ -44,5 +44,19 @@
\include mediaplayer-ecmascript-data-model.qdocinc
- \include mediaplayer-dynamic.qdocinc
+ \section1 Dynamically Loading the State Machine
+
+ We link against the Qt SCXML module by adding the following line to the
+ example \e .pro file:
+
+ \quotefromfile mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
+ \skipto QT
+ \printline scxml
+
+ We dynamically create the state machine in
+ \e mediaplayer-common\Mediaplayer.qml:
+
+ \quotefromfile mediaplayer-common/Mediaplayer.qml
+ \skipto import QtScxml
+ \printuntil scxmlLoader.source
*/
diff --git a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
index 1af874f..f032bba 100644
--- a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
+++ b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
@@ -7,8 +7,6 @@ SOURCES += mediaplayer-qml-dynamic.cpp
RESOURCES += mediaplayer-qml-dynamic.qrc
-load(qscxmlc)
-
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-qml-dynamic
INSTALLS += target
diff --git a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml
index 3e936c3..864a76f 100644
--- a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml
+++ b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml
@@ -49,5 +49,5 @@
****************************************************************************/
Mediaplayer {
- filename: "qrc:///mediaplayer.scxml"
+ source: "qrc:///mediaplayer.scxml"
}
diff --git a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro b/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro
index b6ca140..d8af563 100644
--- a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro
+++ b/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro
@@ -9,8 +9,6 @@ RESOURCES += mediaplayer-qml-static.qrc
STATECHARTS = ../mediaplayer-common/mediaplayer.scxml
-load(qscxmlc)
-
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-qml-static
INSTALLS += target
diff --git a/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc b/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc
index 6cc41df..59ec84f 100644
--- a/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc
+++ b/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc
@@ -44,5 +44,22 @@
\include mediaplayer-ecmascript-data-model.qdocinc
- \include mediaplayer-dynamic.qdocinc
+ \section1 Dynamically Loading the State Machine
+
+ We link against the Qt SCXML module by adding the following line to the
+ example \e .pro file:
+
+ \quotefromfile mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.pro
+ \skipto QT
+ \printuntil scxml
+
+ \quotefromfile mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
+
+ We dynamically create and instantiate the state machine in
+ \e mediaplayer-wigdets-dynamic/mediaplayer-widgets-dynamic.cpp:
+
+ \skipto mainwindow.h
+ \printuntil /^\}/
+
+ \include mediaplayer-widgets-connecting-to-states.qdocinc
*/
diff --git a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp b/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
index eba0fba..457e4ee 100644
--- a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
+++ b/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
@@ -58,16 +58,9 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
auto machine = QScxmlStateMachine::fromFile(QStringLiteral(":mediaplayer.scxml"));
- MainWindow mainWindow;
+ MainWindow mainWindow(machine);
machine->setParent(&mainWindow);
- QObject::connect(&mainWindow, SIGNAL(tap(const QVariant &)),
- machine, SLOT(tap(const QVariant &)));
- QObject::connect(machine, SIGNAL(playbackStarted(const QVariant &)),
- &mainWindow, SLOT(started(const QVariant &)));
- QObject::connect(machine, SIGNAL(playbackStopped(const QVariant &)),
- &mainWindow, SLOT(stopped(const QVariant &)));
-
machine->start();
mainWindow.show();
return app.exec();
diff --git a/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc b/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc
index 9d1ae39..dfa1230 100644
--- a/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc
+++ b/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc
@@ -55,9 +55,5 @@
\skipto mediaplayer.h
\printuntil MainWindow mainWindow
- To be notified when a state machine sends out an event, we connect to the
- corresponding signals. The media player state machine will send out events
- when users tap a control and when playback starts or stops:
-
- \printuntil MainWindow::stopped
+ \include mediaplayer-widgets-connecting-to-states.qdocinc
*/
diff --git a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp b/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp
index 1668eac..6a049d9 100644
--- a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp
+++ b/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp
@@ -58,14 +58,7 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
MediaPlayerStateMachine machine;
- MainWindow mainWindow;
-
- QObject::connect(&mainWindow, &MainWindow::tap,
- &machine, &MediaPlayerStateMachine::tap);
- QObject::connect(&machine, &MediaPlayerStateMachine::playbackStarted,
- &mainWindow, &MainWindow::started);
- QObject::connect(&machine, &MediaPlayerStateMachine::playbackStopped,
- &mainWindow, &MainWindow::stopped);
+ MainWindow mainWindow(&machine);
machine.start();
mainWindow.show();
diff --git a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro b/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro
index af87491..89a7e4a 100644
--- a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro
+++ b/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro
@@ -17,5 +17,3 @@ HEADERS += \
# install
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-widgets-static
INSTALLS += target
-
-load(qscxmlc)
diff --git a/examples/scxml/pinball/doc/src/pinball.qdoc b/examples/scxml/pinball/doc/src/pinball.qdoc
index b3566eb..5cf9f1d 100644
--- a/examples/scxml/pinball/doc/src/pinball.qdoc
+++ b/examples/scxml/pinball/doc/src/pinball.qdoc
@@ -344,6 +344,15 @@
\printuntil updateLights
\dots 20
\skipto /^\ {16}<\//
+ \printuntil updateLightsAccordingToLettersState
+ \dots 20
+ \skipto /^\ {16}<\//
+ \printuntil turnOnLights
+ \dots 20
+ \skipto /^\ {16}<\//
+ \printuntil turnOffLights
+ \dots 20
+ \skipto /^\ {16}<\//
\printuntil /^\ {12}<\//
The \c lightImpulseGenerator contains two child states:
@@ -405,39 +414,21 @@
the machine to activate the \c jackpotStateOn.
\skipto updateLights
- \printuntil rLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil aLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil zLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil yLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil rLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil aLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil zLetterOn
- \dots 32
- \skipto /^\ {28}<\//
- \printuntil yLetterOn
- \dots 32
- \skipto /^\ {28}<\//
+ \printuntil /^\ {16}<\//
+ \printuntil updateLightsAccordingToLettersState
+ \printuntil /^\ {16}<\//
+ \printuntil turnOnLights
+ \printuntil /^\ {16}<\//
+ \printuntil turnOffLights
\printuntil /^\ {16}<\//
When we receive the \c updateLights event, we first want to send a
- \c updateScore signal outside of the state machine. We pass
- the current values of the \c highScore and \c score variables to the signal.
- This signal is received by the C++ part.
+ \c updateScore event outside of the state machine. We pass
+ the current values of the \c highScore and \c score variables to the event.
+ This event is received by the C++ part.
Next, depending on whether we are in \c jackpotStateOn or \c jackpotStateOff,
- we send the \c turnOnJackpot or the \c turnOffJackpot signal,
+ we send the \c turnOnJackpot or the \c turnOffJackpot event,
which instructs the \c guiControl state to transition to
\c jackpotLightOn or \c jackpotLightOff, respectively.
@@ -463,11 +454,11 @@
The class is declared in \e mainwindow.h.
\quotefromfile pinball/mainwindow.h
- \skipto Pinball
+ \skipto MainWindow
\printuntil };
The \c MainWindow class holds the pointer to the
- \c {Pinball *m_machine} which is the state machine
+ \c {QScxmlStateMachine *m_machine} which is the state machine
class automatically generated by Qt out of SCMXL file
and the pointer to the \c {Ui::MainWindow *m_ui} which
describes the GUI part. It also declares two helper methods.
@@ -478,7 +469,7 @@
The constructor of the \c MainWindow class
instantiates the GUI part of the application
- and stores the pointer to the passed \c Pinball state machine.
+ and stores the pointer to the passed \c QScxmlStateMachine.
It also initializes the GUI part and glues the
GUI part to the state machine by connecting
their communication interfaces together.
@@ -491,10 +482,9 @@
the widget is disabled. We do that for all lights, targets,
and description labels.
- We also connect to the \c eventOccurred() signal, propagated
- by the state machine and intercept the \c updateScore
- event sent from the SCXML document in order to update
- the score displays with the values passed with the event.
+ We also intercept the \c updateScore event sent by the state machine,
+ in order to update the score displays with the values
+ passed with the event.
The info about hitting any GUI target needs to be passed
to the state machine and we do that by connecting
diff --git a/examples/scxml/pinball/mainwindow.cpp b/examples/scxml/pinball/mainwindow.cpp
index f2887f0..777de8a 100644
--- a/examples/scxml/pinball/mainwindow.cpp
+++ b/examples/scxml/pinball/mainwindow.cpp
@@ -52,11 +52,11 @@
#include "ui_mainwindow.h"
#include <QStringListModel>
-#include "pinball.h"
+#include <QScxmlStateMachine>
QT_USE_NAMESPACE
-MainWindow::MainWindow(Pinball *machine, QWidget *parent) :
+MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
QWidget(parent),
m_ui(new Ui::MainWindow),
m_machine(machine)
@@ -89,8 +89,13 @@ MainWindow::MainWindow(Pinball *machine, QWidget *parent) :
initAndConnect(QLatin1String("onState"), m_ui->ballOutButton);
// datamodel update
- connect(m_machine, SIGNAL(eventOccurred(const QScxmlEvent &)),
- this, SLOT(eventOccurred(const QScxmlEvent &)));
+ m_machine->connectToEvent("updateScore", [this] (const QScxmlEvent &event) {
+ const QVariant data = event.data();
+ const QString highScore = data.toMap().value("highScore").toString();
+ m_ui->highScoreLabel->setText(highScore);
+ const QString score = data.toMap().value("score").toString();
+ m_ui->scoreLabel->setText(score);
+ });
// gui interaction
connect(m_ui->cButton, &QAbstractButton::clicked,
@@ -124,21 +129,5 @@ MainWindow::~MainWindow()
void MainWindow::initAndConnect(const QString &state, QWidget *widget)
{
widget->setEnabled(m_machine->isActive(state));
- m_machine->connectToState(state, widget, SLOT(setEnabled(bool)));
+ m_machine->connectToState(state, widget, &QWidget::setEnabled);
}
-
-void MainWindow::eventOccurred(const QScxmlEvent &event)
-{
- if (event.originType() != QLatin1String("qt:signal"))
- return;
-
- if (event.name() != QLatin1String("updateScore"))
- return;
-
- const QVariant data = event.data();
- const QString highScore = data.toMap().value("highScore").toString();
- m_ui->highScoreLabel->setText(highScore);
- const QString score = data.toMap().value("score").toString();
- m_ui->scoreLabel->setText(score);
-}
-
diff --git a/examples/scxml/pinball/mainwindow.h b/examples/scxml/pinball/mainwindow.h
index 174248c..57dffe7 100644
--- a/examples/scxml/pinball/mainwindow.h
+++ b/examples/scxml/pinball/mainwindow.h
@@ -57,26 +57,22 @@ QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
-class QScxmlEvent;
+class QScxmlStateMachine;
QT_END_NAMESPACE
-class Pinball;
class MainWindow : public QWidget
{
Q_OBJECT
public:
- explicit MainWindow(Pinball *machine, QWidget *parent = 0);
+ explicit MainWindow(QScxmlStateMachine *machine, QWidget *parent = 0);
~MainWindow();
-private slots:
- void eventOccurred(const QScxmlEvent &event);
-
private:
void initAndConnect(const QString &state, QWidget *widget);
QT_PREPEND_NAMESPACE(Ui::MainWindow) *m_ui;
- Pinball *m_machine;
+ QScxmlStateMachine *m_machine;
};
#endif // MAINWINDOW_H
diff --git a/examples/scxml/pinball/pinball.pro b/examples/scxml/pinball/pinball.pro
index 2cb002d..549010d 100644
--- a/examples/scxml/pinball/pinball.pro
+++ b/examples/scxml/pinball/pinball.pro
@@ -17,5 +17,3 @@ HEADERS += \
# install
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/pinball
INSTALLS += target
-
-load(qscxmlc)
diff --git a/examples/scxml/pinball/pinball.scxml b/examples/scxml/pinball/pinball.scxml
index 1ec7487..022f202 100644
--- a/examples/scxml/pinball/pinball.scxml
+++ b/examples/scxml/pinball/pinball.scxml
@@ -50,7 +50,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: no -->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="Pinball" datamodel="ecmascript">
<datamodel>
@@ -265,7 +264,7 @@
</transition>
<transition event="updateLights">
- <send type="qt:signal" event="updateScore">
+ <send event="updateScore">
<param name="highScore" expr="highScore"/>
<param name="score" expr="score"/>
</send>
@@ -277,97 +276,77 @@
<if cond="In('lightImpulseOn')">
<if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
+ <raise event="turnOnLights"/>
<raise event="turnOnHurry"/>
<raise event="turnOnJackpot"/>
<raise event="turnOnGameOver"/>
<elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
+ <raise event="updateLightsAccordingToLettersState"/>
<raise event="turnOffHurry"/>
<raise event="turnOffGameOver"/>
<else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
+ <raise event="turnOnLights"/>
<raise event="turnOnHurry"/>
<raise event="turnOffGameOver"/>
</if>
<else/>
<if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
+ <raise event="turnOffLights"/>
<raise event="turnOffHurry"/>
<raise event="turnOffJackpot"/>
<elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
+ <raise event="turnOffLights"/>
<else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
+ <raise event="updateLightsAccordingToLettersState"/>
</if>
<raise event="turnOffHurry"/>
<raise event="turnOffGameOver"/>
</if>
</transition>
+
+ <transition event="updateLightsAccordingToLettersState">
+ <if cond="In('cLetterOn')">
+ <raise event="turnOnC"/>
+ <else/>
+ <raise event="turnOffC"/>
+ </if>
+ <if cond="In('rLetterOn')">
+ <raise event="turnOnR"/>
+ <else/>
+ <raise event="turnOffR"/>
+ </if>
+ <if cond="In('aLetterOn')">
+ <raise event="turnOnA"/>
+ <else/>
+ <raise event="turnOffA"/>
+ </if>
+ <if cond="In('zLetterOn')">
+ <raise event="turnOnZ"/>
+ <else/>
+ <raise event="turnOffZ"/>
+ </if>
+ <if cond="In('yLetterOn')">
+ <raise event="turnOnY"/>
+ <else/>
+ <raise event="turnOffY"/>
+ </if>
+ </transition>
+
+ <transition event="turnOnLights">
+ <raise event="turnOnC"/>
+ <raise event="turnOnR"/>
+ <raise event="turnOnA"/>
+ <raise event="turnOnZ"/>
+ <raise event="turnOnY"/>
+ </transition>
+
+ <transition event="turnOffLights">
+ <raise event="turnOffC"/>
+ <raise event="turnOffR"/>
+ <raise event="turnOffA"/>
+ <raise event="turnOffZ"/>
+ <raise event="turnOffY"/>
+ </transition>
</state>
</parallel>
</parallel>
diff --git a/examples/scxml/scxml.pro b/examples/scxml/scxml.pro
index 7a93fd4..8da9db6 100644
--- a/examples/scxml/scxml.pro
+++ b/examples/scxml/scxml.pro
@@ -1,6 +1,5 @@
TEMPLATE = subdirs
-!msvc{
qtHaveModule(widgets) {
SUBDIRS += trafficlight-widgets-static
SUBDIRS += trafficlight-widgets-dynamic
@@ -8,12 +7,14 @@ qtHaveModule(widgets) {
SUBDIRS += mediaplayer-widgets-dynamic
SUBDIRS += calculator-widgets
SUBDIRS += pinball
+ SUBDIRS += sudoku
}
qtHaveModule(qml) {
SUBDIRS += calculator-qml
SUBDIRS += trafficlight-qml-static
SUBDIRS += trafficlight-qml-dynamic
+ SUBDIRS += trafficlight-qml-simple
SUBDIRS += mediaplayer-qml-static
SUBDIRS += mediaplayer-qml-dynamic
@@ -21,4 +22,5 @@ qtHaveModule(qml) {
SUBDIRS += invoke-static
SUBDIRS += invoke-dynamic
}
-}
+
+SUBDIRS += ftpclient
diff --git a/examples/scxml/sudoku/data/nearly-solved-sudoku.data b/examples/scxml/sudoku/data/nearly-solved-sudoku.data
new file mode 100644
index 0000000..0e5c31f
--- /dev/null
+++ b/examples/scxml/sudoku/data/nearly-solved-sudoku.data
@@ -0,0 +1,9 @@
+0, 3, 5, 2, 6, 9, 7, 8, 1
+6, 8, 2, 5, 7, 1, 4, 9, 3
+1, 9, 7, 8, 3, 4, 5, 6, 2
+8, 2, 6, 1, 9, 5, 3, 4, 7
+3, 7, 4, 6, 8, 2, 9, 1, 5
+9, 5, 1, 7, 4, 3, 6, 2, 8
+5, 1, 9, 3, 2, 6, 8, 7, 4
+2, 4, 8, 9, 5, 7, 1, 3, 6
+7, 6, 3, 4, 1, 8, 2, 5, 9
diff --git a/examples/scxml/sudoku/data/sudoku.data b/examples/scxml/sudoku/data/sudoku.data
new file mode 100644
index 0000000..342fdf0
--- /dev/null
+++ b/examples/scxml/sudoku/data/sudoku.data
@@ -0,0 +1,9 @@
+0, 0, 0, 2, 6, 0, 7, 0, 1
+6, 8, 0, 0, 7, 0, 0, 9, 0
+1, 9, 0, 0, 0, 4, 5, 0, 0
+8, 2, 0, 1, 0, 0, 0, 4, 0
+0, 0, 4, 6, 0, 2, 9, 0, 0
+0, 5, 0, 0, 0, 3, 0, 2, 8
+0, 0, 9, 3, 0, 0, 0, 7, 4
+0, 4, 0, 0, 5, 0, 0, 3, 6
+7, 0, 3, 0, 1, 8, 0, 0, 0
diff --git a/examples/scxml/sudoku/doc/images/sudoku.png b/examples/scxml/sudoku/doc/images/sudoku.png
new file mode 100644
index 0000000..ef6d113
--- /dev/null
+++ b/examples/scxml/sudoku/doc/images/sudoku.png
Binary files differ
diff --git a/examples/scxml/sudoku/doc/src/sudoku.qdoc b/examples/scxml/sudoku/doc/src/sudoku.qdoc
new file mode 100644
index 0000000..c3d8971
--- /dev/null
+++ b/examples/scxml/sudoku/doc/src/sudoku.qdoc
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example sudoku
+ \title Qt SCXML Sudoku Example
+ \ingroup examples-qtscxml
+ \brief Presents the use of SCXML in a sudoku game.
+
+ \e {Sudoku} demonstrates how an SCXML file may be used in a game.
+
+ \include examples-run.qdocinc
+
+ \section1 Sudoku Features
+
+ \image sudoku.png Screenshot of the Sudoku example
+
+ Our sudoku contains the following features:
+ \list
+ \li Initially and when the game ends, the sudoku
+ enters the \c idle state. In that state the players
+ can see if their last game finished successfully or not.
+ The state machine is then in one of two child states
+ of the \c idle state: \c solved or \c unsolved,
+ respectively. In the \c idle state the players
+ can also choose the sudoku grid they would like to solve.
+ The grid is disabled and the user interaction
+ is ignored.
+ \li After players click the \uicontrol Start button, the sudoku
+ enters the \c playing state and is ready for the user
+ interaction on the board.
+ \li When the game is in the \c playing state and the players
+ click the \uicontrol Stop button, the game ends
+ and enters the \c unsolved child state of the \c idle state.
+ If the players have solved the current puzzle successfully,
+ the game automatically ends and enters the \c solved child state
+ of the \c idle state indicating success.
+ \li The board consist of 81 buttons, laid out in a 9x9 grid.
+ The buttons with initial values given remain disabled
+ during the game. The players can only interact
+ with buttons initially empty. Each
+ click on the button increases its value by one.
+ \li During the game the \uicontrol Undo button
+ is available for the players' convenience.
+ \endlist
+
+ \section1 SCXML Part: Internal Logic Description
+
+ The \e sudoku.scxml file describes the internal structure of
+ the states the sudoku game can be in, defines the transitions
+ between states, and triggers the appropriate script
+ functions when the transitions take place. It also
+ communicates with the GUI part by sending events and listening
+ to the upcoming events and reacting to them.
+
+ We use the ECMAScript data model:
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto scxml
+ \printuntil ecmascript
+
+ We declare the following variables:
+
+ \printuntil </datamodel>
+
+ \table
+ \header
+ \li Variable
+ \li Description
+ \row
+ \li \c initState
+ \li Holds the initial state of the current game. It is a
+ two-dimensional array of 9x9 cells that contain initial sudoku
+ numbers. The value of zero means the cell is initially empty.
+ \row
+ \li \c currentState
+ \li Holds the current state of the game being played. It is
+ similar to the \c initState variable and initially
+ contains the same content. However, when the players start
+ entering the numbers into the empty cells, this variable
+ is being updated accordingly, while the \c initState variable
+ remains unchanged.
+ \row
+ \li \c undoStack
+ \li Holds the history of players' moves. It is a vector of
+ the cells' coordinates that were touched last. Each
+ new modification during a game adds a pair of
+ x and y coordinates to that vector.
+ \endtable
+
+ The variables above are shared with the script helper functions
+ defined in the \c sudoku.js file:
+
+ \printuntil sudoku.js
+
+ We call some of the functions defined there
+ when taking transitions or in reaction to the
+ events sent by the GUI.
+
+ All the possible states mentioned before are defined in a
+ root state \c game.
+
+ \printuntil idle
+ \dots 12
+ \skipto id="unsolved"
+ \printuntil playing
+ \dots 12
+ \skipto /^\ {8}<\//
+ \printline state
+ \dots 8
+ \skipto /^\ {4}<\//
+ \printline state
+
+ When the sudoku example is started, the state
+ machine enters the \c game state and stays in this
+ state until the application exits. When entering this
+ state, we raise internally the \c restart event.
+ This event is also being raised whenever the players
+ change the current sudoku grid or when they start
+ the game by pressing the \uicontrol Start button.
+ We do not want to send it when they have finished
+ the current game because we still want to show the
+ filled grid from the last game play. So, this event
+ is being raised from three different contexts
+ and is captured internally once in a targetless
+ transition of the \c game state:
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto <transition event="restart">
+ \printuntil </transition>
+
+ When we catch the \c restart event, we call
+ a helper \c restart() script method, defined in the \c sudoku.js
+ file and raise internally an additional \c update event.
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto restart
+ \printuntil }
+
+ The \c restart() function assigns the \c initState into the \c currentState
+ variable and clears the \c undoStack variable.
+
+ The \c update event is raised internally whenever we want to notify the GUI
+ that the grid contents have been changed and that
+ the GUI should update itself according to the passed values. This event
+ is caught in another targetless transition of the \c game state:
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto <transition event="update">
+ \printuntil </transition>
+
+ We send the external event \c updateGUI,
+ which is being intercepted in the \l {cpp}{C++ code}.
+ The \c updateGUI event is equipped
+ with additional data, specified inside \c <param>
+ elements. We pass two parameters, which
+ are accessible externally through the
+ \c currentState and \c initState names.
+ The actual values passed for them
+ equal the datamodel's \c currentState
+ and \c initState variables, respectively, which
+ are specified by the \c expr attributes.
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto idle
+ \printuntil </state>
+
+ When in \c idle state, we react to two
+ events, which may be sent by the GUI part:
+ \c start and \c setup. Whenever we receive the
+ \c start event, we just transition to the \c playing
+ state. When we receive the \c setup event, we
+ expect that the GUI part has sent us the new grid
+ to be solved. The grid's new initial state is expected
+ to be passed through the \c initState field of
+ \c _event.data. We assign the passed value to the
+ \c initState variable defined in our datamodel and
+ restart the grid's contents.
+
+ \skipto playing
+ \printuntil </transition>
+ \dots 12
+ \skipto </state>
+ \printline </state>
+
+ Whenever we enter the \c playing state, we reset
+ the grid's contents since we could have been still
+ showing the contents from the previous game play.
+ In the \c playing state we react to possible
+ events sent from the GUI: \c tap, \c undo, and \c stop.
+
+ The \c tap event is sent when the players
+ press one of the enabled sudoku cells.
+ This event is expected to contain additional data
+ specifying the cell's coordinates, which are passed
+ through the \c x and \c y fields of \c _event.data.
+ First, we check if the passed coordinates are valid
+ by invoking the \c isValidPosition() script function:
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto isValidPosition
+ \printuntil }
+
+ We ensure the coordinates are neither negative nor bigger than our grid.
+ In addition, we check if the coordinates point to an
+ initially empty cell, since we can not modify
+ the cells initially given by the grid description.
+
+ When we have ensured the passed coordinates are correct,
+ we call \c calculateCurrentState() script function:
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto calculateCurrentState
+ \printuntil }
+
+ This function increments the value of the passed
+ grid's cell and adds the new move to the
+ undo stack history.
+
+ Right after the \c calculateCurrentState() function
+ finishes its execution, we check whether the grid is
+ already solved by calling the \c isSolved() script function:
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto isOK
+ \printuntil return true
+ \printuntil }
+ \skipto isSolved
+ \printuntil return true
+ \printuntil }
+
+ The \c isSolved() function returns \c true if the
+ grid is properly solved. Since we need to check
+ each row, each column, and each 3x3 square, we define
+ the \c isOK() helper function. This function takes
+ the vector of numbers and returns \c true if the passed vector
+ contains unique numbers and no number equals zero, meaning
+ there is no empty cell. The main loop of the \c isSolved() is invoked
+ nine times. In every iteration, we construct three vectors of numbers
+ representing a row, a column, and a square of the grid and call \c isOK()
+ for them. When all 27 vectors are OK, the grid is solved properly
+ and we return \c true.
+
+ Coming back to our SCXML file, in case \c isSolved()
+ returns \c true, we raise the \c solved event internally.
+ The last instruction in case of a proper move is to raise
+ the \c update event, since we need to notify the GUI
+ about the grid's change.
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto id="playing"
+ \printline playing
+ \dots 12
+ \skipto undo
+ \printuntil </state>
+
+ When in the \c playing state, we also react to the \c undo
+ event sent from the GUI. In this case, we call the \c undo()
+ script function and notify the GUI about the need of an update.
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto function undo
+ \printuntil }
+
+ The \c undo() function removes the last move from
+ the history, if there was any, and decrements the current value
+ for the cell described by the coordinates taken from this move.
+
+ The \c playing state is also ready for the \c stop
+ event sent by the GUI when the players press the \uicontrol Stop
+ button. In this case, we simply activate the \c idle state.
+
+ In addition, we intercept the \c solved event
+ sent internally and activate the \c solved state in this case.
+
+ \target cpp
+ \section1 C++ Part: Constructing the GUI
+
+ The C++ part of the application consists of a
+ \c MainWindow class which constructs the GUI and glues it with the SCXML part.
+ The class is declared in \e mainwindow.h.
+
+ \quotefromfile sudoku/mainwindow.h
+ \skipto MainWindow
+ \printuntil };
+
+ The \c MainWindow class holds the pointer to the
+ \c {QScxmlStateMachine *m_machine}, which is the state machine
+ class automatically generated by Qt out of the \c sudoku.scxml file.
+ It also holds the pointers to some GUI elements.
+
+ \quotefromfile sudoku/mainwindow.cpp
+ \skipto MainWindow
+ \printuntil {
+
+ The constructor of the \c MainWindow class
+ instantiates the GUI part of the application
+ and stores the pointer to the passed state machine.
+ It also initializes the GUI part and glues the
+ GUI part to the state machine by connecting
+ their communication interfaces together.
+
+ \skipto clicked
+ \printuntil });
+
+ First, we create 81 buttons and connect
+ their \c clicked signal to a lambda expression
+ that submits the \c tap event to the state machine
+ passing the button's coordinates.
+
+ Later, we add some horizontal and vertical lines
+ to the grid in order to group buttons in 3x3 boxes.
+
+ \skipto clicked
+ \printuntil });
+
+ We create the \uicontrol {Start / Stop} button and
+ connect its clicked signal to a lambda expression
+ which submits the \c stop or \c start event
+ depending on whether the machine is in \c playing state
+ or not, respectively.
+
+ We create a label informing whether the grid is solved
+ or not, and an \uicontrol Undo button, which submits the
+ \c undo event whenever it is clicked.
+
+ \skipto clicked
+ \printuntil });
+
+ Then we create a combobox that is filled with
+ grid names to be solved. These grids are
+ read from the \c :/data directory of the application
+ compiled-in resources.
+
+ \skipto currentIndexChanged
+ \printuntil setInitialValues
+
+ Whenever the players change the grid in the combobox,
+ we read the grid contents storing it in the
+ variant map under the \c initValues key as a
+ list of lists of int variants and we submit the
+ \c setup event to the state machine passing
+ the grid's contents. Initially, we read the first
+ available grid from the list and pass it directly
+ to the sudoku state machine as the initial grid.
+
+ \skipto connectToState
+ \printline playing
+ \dots 8
+ \skipto });
+ \printuntil connectToEvent
+ \dots 8
+ \skipto });
+ \printline });
+
+ Later, we connect to the signals that are being sent
+ whenever the machine enters or leaves the \c playing
+ or \c solved states, and we update some GUI parts accordingly.
+ We also connect to the state machine's \c updateGUI event
+ and update all the buttons' values according to the passed cells' states.
+
+ \quotefromfile sudoku/main.cpp
+ \skipto #include
+ \printuntil /\}$/
+
+ In the \c main() function in the \c main.cpp file, we instantiate the
+ \c app application object, \c Sudoku state machine,
+ and \c MainWindow GUI class. We start the state machine,
+ show the main window, and execute the application.
+*/
diff --git a/tests/auto/qscxmlc/data/invalidRoot2.scxml b/examples/scxml/sudoku/main.cpp
index 9d6cce0..0af8d72 100644
--- a/tests/auto/qscxmlc/data/invalidRoot2.scxml
+++ b/examples/scxml/sudoku/main.cpp
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,30 +47,20 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content>
- <scxmn name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there"datamodel <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
+#include "sudoku.h"
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ Sudoku machine;
+ MainWindow mainWindow(&machine);
+
+ machine.start();
+ mainWindow.show();
+ return app.exec();
+}
diff --git a/examples/scxml/sudoku/mainwindow.cpp b/examples/scxml/sudoku/mainwindow.cpp
new file mode 100644
index 0000000..3a13010
--- /dev/null
+++ b/examples/scxml/sudoku/mainwindow.cpp
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+
+#include <QStringListModel>
+#include <QScxmlStateMachine>
+#include <QComboBox>
+#include <QToolButton>
+#include <QLabel>
+#include <QGridLayout>
+#include <QFile>
+#include <QDir>
+#include <QTextStream>
+
+static int Size = 9;
+
+QT_USE_NAMESPACE
+
+static QVariantList emptyRow()
+{
+ QVariantList row;
+ for (int i = 0; i < Size; i++)
+ row.append(QVariant(0));
+ return row;
+}
+
+static QVariantMap readSudoku(const QString &fileName)
+{
+ QFile input(fileName);
+ input.open(QIODevice::ReadOnly | QIODevice::Text);
+ QTextStream str(&input);
+ const QString data = str.readAll();
+
+ QVariantList initRowsVariant;
+ const QStringList rows = data.split(QLatin1Char('\n'));
+ for (int i = 0; i < Size; i++) {
+ if (i < rows.count()) {
+ QVariantList initRowVariant;
+ const QStringList row = rows.at(i).split(QLatin1Char(','));
+ for (int j = 0; j < Size; j++) {
+ const int val = j < row.count()
+ ? row.at(j).toInt() % (Size + 1) : 0;
+ initRowVariant.append(val);
+ }
+ initRowsVariant.append(QVariant(initRowVariant));
+ } else {
+ initRowsVariant.append(QVariant(emptyRow()));
+ }
+ }
+
+ QVariantMap dataVariant;
+ dataVariant.insert(QStringLiteral("initState"), initRowsVariant);
+
+ return dataVariant;
+}
+
+MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
+ QWidget(parent),
+ m_machine(machine)
+{
+ const QVector<QToolButton *> initVector(Size, nullptr);
+ m_buttons = QVector<QVector<QToolButton *> >(Size, initVector);
+
+ QGridLayout *layout = new QGridLayout(this);
+
+ for (int i = 0; i < Size; i++) {
+ for (int j = 0; j < Size; j++) {
+ QToolButton *button = new QToolButton(this);
+ button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ layout->addWidget(button, i + i / 3, j + j / 3);
+ m_buttons[i][j] = button;
+ connect(button, &QToolButton::clicked, [this, i, j] () {
+ QVariantMap data;
+ data.insert(QStringLiteral("x"), i);
+ data.insert(QStringLiteral("y"), j);
+ m_machine->submitEvent("tap", data);
+ });
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 2; j++) {
+ QFrame *hFrame = new QFrame(this);
+ hFrame->setFrameShape(QFrame::HLine);
+ layout->addWidget(hFrame, 4 * j + 3, 4 * i, 1, 3);
+
+ QFrame *vFrame = new QFrame(this);
+ vFrame->setFrameShape(QFrame::VLine);
+ layout->addWidget(vFrame, 4 * i, 4 * j + 3, 3, 1);
+ }
+ }
+
+ m_startButton = new QToolButton(this);
+ m_startButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_startButton->setText(tr("Start"));
+ layout->addWidget(m_startButton, Size + 3, 0, 1, 3);
+
+ connect(m_startButton, &QAbstractButton::clicked,
+ [this] {
+ if (m_machine->isActive("playing"))
+ m_machine->submitEvent("stop");
+ else
+ m_machine->submitEvent("start");
+ });
+
+ m_label = new QLabel(tr("unsolved"));
+ m_label->setAlignment(Qt::AlignCenter);
+ layout->addWidget(m_label, Size + 3, 4, 1, 3);
+
+ m_undoButton = new QToolButton(this);
+ m_undoButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_undoButton->setText(tr("Undo"));
+ m_undoButton->setEnabled(false);
+ layout->addWidget(m_undoButton, Size + 3, 8, 1, 3);
+
+ connect(m_undoButton, &QAbstractButton::clicked,
+ [this] {
+ m_machine->submitEvent("undo");
+ });
+
+ m_chooser = new QComboBox(this);
+ layout->addWidget(m_chooser, Size + 4, 0, 1, 11);
+
+ QDir dataDir(QLatin1String(":/data"));
+ QFileInfoList sudokuFiles = dataDir.entryInfoList(QStringList() << "*.data");
+ foreach (const QFileInfo &sudokuFile, sudokuFiles)
+ m_chooser->addItem(sudokuFile.completeBaseName(), sudokuFile.absoluteFilePath());
+
+ connect(m_chooser, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ [this] (int index) {
+ const QString sudokuFile = m_chooser->itemData(index).toString();
+ const QVariantMap initValues = readSudoku(sudokuFile);
+ m_machine->submitEvent("setup", initValues);
+ });
+
+ const QVariantMap initValues = readSudoku(m_chooser->itemData(0).toString());
+ m_machine->setInitialValues(initValues);
+
+ m_machine->connectToState("playing", [this] (bool playing) {
+ if (playing) {
+ m_startButton->setText(tr("Stop"));
+ m_undoButton->setEnabled(true);
+ m_chooser->setEnabled(false);
+ } else {
+ m_startButton->setText(tr("Start"));
+ m_undoButton->setEnabled(false);
+ m_chooser->setEnabled(true);
+ }
+ });
+
+ m_machine->connectToState("solved", [this] (bool solved) {
+ if (solved)
+ m_label->setText(tr("SOLVED !!!"));
+ else
+ m_label->setText(tr("unsolved"));
+ });
+
+ m_machine->connectToEvent("updateGUI", [this] (const QScxmlEvent &event) {
+ const QVariant data = event.data();
+
+ const QVariantList currentRows = data.toMap().value("currentState").toList();
+ for (int i = 0; i < currentRows.count(); i++) {
+ const QVariantList row = currentRows.at(i).toList();
+ for (int j = 0; j < row.count(); j++) {
+ const int value = row.at(j).toInt();
+ const QString text = value ? QString::number(value) : QString();
+ m_buttons[i][j]->setText(text);
+ }
+ }
+
+ const bool active = m_machine->isActive("playing");
+
+ const QVariantList initRows = data.toMap().value("initState").toList();
+ for (int i = 0; i < initRows.count(); i++) {
+ const QVariantList row = initRows.at(i).toList();
+ for (int j = 0; j < row.count(); j++) {
+ const int value = row.at(j).toInt();
+ const bool enabled = !value && active; // enable only zeroes from initState
+ m_buttons[i][j]->setEnabled(enabled);
+ }
+ }
+ });
+
+ setLayout(layout);
+}
+
+MainWindow::~MainWindow()
+{
+}
+
diff --git a/tests/auto/qscxmlc/data/noContentInInvoke.scxml b/examples/scxml/sudoku/mainwindow.h
index 961a71e..99a0f1b 100644
--- a/tests/auto/qscxmlc/data/noContentInInvoke.scxml
+++ b/examples/scxml/sudoku/mainwindow.h
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,31 +47,35 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <contenscxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
-  <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QToolButton;
+class QScxmlStateMachine;
+class QLabel;
+class QComboBox;
+QT_END_NAMESPACE
+
+
+class MainWindow : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QScxmlStateMachine *machine, QWidget *parent = 0);
+ ~MainWindow();
+
+private:
+ QScxmlStateMachine *m_machine;
+ QVector<QVector<QToolButton *> > m_buttons;
+ QToolButton *m_startButton;
+ QToolButton *m_undoButton;
+ QLabel *m_label;
+ QComboBox *m_chooser;
+};
+
+#endif // MAINWINDOW_H
diff --git a/examples/scxml/sudoku/sudoku.js b/examples/scxml/sudoku/sudoku.js
new file mode 100644
index 0000000..21a1ed8
--- /dev/null
+++ b/examples/scxml/sudoku/sudoku.js
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+function restart() {
+ for (var i = 0; i < initState.length; i++)
+ currentState[i] = initState[i].slice();
+ undoStack = [];
+}
+
+function isValidPosition() {
+ var x = _event.data.x;
+ var y = _event.data.y;
+ if (x < 0 || x >= initState.length)
+ return false;
+ if (y < 0 || y >= initState.length)
+ return false;
+ if (initState[x][y] !== 0)
+ return false;
+ return true;
+}
+
+function calculateCurrentState() {
+ if (isValidPosition() === false)
+ return;
+ var x = _event.data.x;
+ var y = _event.data.y;
+ var currentValue = currentState[x][y];
+ if (currentValue === initState.length)
+ currentValue = 0;
+ else
+ currentValue += 1;
+ currentState[x][y] = currentValue;
+ undoStack.push([x, y]);
+}
+
+function isOK(numbers) {
+ var temp = [];
+ for (var i = 0; i < numbers.length; i++) {
+ var currentValue = numbers[i];
+ if (currentValue === 0)
+ return false;
+ if (temp.indexOf(currentValue) >= 0)
+ return false;
+ temp.push(currentValue);
+ }
+ return true;
+}
+
+function isSolved() {
+ for (var i = 0; i < currentState.length; i++) {
+ if (!isOK(currentState[i]))
+ return false;
+
+ var column = [];
+ var square = [];
+ for (var j = 0; j < currentState[i].length; j++) {
+ column.push(currentState[j][i]);
+ square.push(currentState[Math.floor(i / 3) * 3 + Math.floor(j / 3)]
+ [i % 3 * 3 + j % 3]);
+ }
+
+ if (!isOK(column))
+ return false;
+ if (!isOK(square))
+ return false;
+ }
+ return true;
+}
+
+function undo() {
+ if (!undoStack.length)
+ return;
+
+ var lastMove = undoStack.pop();
+ var x = lastMove[0];
+ var y = lastMove[1];
+ var currentValue = currentState[x][y];
+ if (currentValue === 0)
+ currentValue = initState.length;
+ else
+ currentValue -= 1;
+ currentState[x][y] = currentValue;
+}
diff --git a/examples/scxml/sudoku/sudoku.pro b/examples/scxml/sudoku/sudoku.pro
new file mode 100644
index 0000000..0853a5e
--- /dev/null
+++ b/examples/scxml/sudoku/sudoku.pro
@@ -0,0 +1,19 @@
+QT += widgets scxml
+
+CONFIG += c++11
+
+STATECHARTS = sudoku.scxml
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp
+
+HEADERS += \
+ mainwindow.h
+
+RESOURCES += \
+ sudoku.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/scxml/sudoku
+INSTALLS += target
diff --git a/examples/scxml/sudoku/sudoku.qrc b/examples/scxml/sudoku/sudoku.qrc
new file mode 100644
index 0000000..6a15532
--- /dev/null
+++ b/examples/scxml/sudoku/sudoku.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/">
+ <file>sudoku.js</file>
+ <file>data/sudoku.data</file>
+ <file>data/nearly-solved-sudoku.data</file>
+ </qresource>
+</RCC>
+
diff --git a/examples/scxml/sudoku/sudoku.scxml b/examples/scxml/sudoku/sudoku.scxml
new file mode 100644
index 0000000..eb36283
--- /dev/null
+++ b/examples/scxml/sudoku/sudoku.scxml
@@ -0,0 +1,111 @@
+<?xml version="1.0" ?>
+<!--
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
+ name="Sudoku" datamodel="ecmascript">
+ <datamodel>
+ <data id="initState"/>
+ <data id="currentState" expr="[[]]"/>
+ <data id="undoStack"/>
+ </datamodel>
+ <script src="sudoku.js"/>
+ <state id="game">
+ <onentry>
+ <raise event="restart"/>
+ </onentry>
+ <state id="idle">
+ <transition event="start" target="playing"/>
+ <transition event="setup" target="unsolved">
+ <assign location="initState" expr="_event.data.initState"/>
+ <raise event="restart"/>
+ </transition>
+ <state id="unsolved"/>
+ <state id="solved"/>
+ </state>
+ <state id="playing">
+ <onentry>
+ <raise event="restart"/>
+ </onentry>
+ <transition event="tap">
+ <if cond="isValidPosition()">
+ <script>
+ calculateCurrentState();
+ </script>
+ <if cond="isSolved()">
+ <raise event="solved"/>
+ </if>
+ <raise event="update"/>
+ </if>
+ </transition>
+ <transition event="undo">
+ <script>
+ undo();
+ </script>
+ <raise event="update"/>
+ </transition>
+ <transition event="stop" target="idle"/>
+ <transition event="solved" target="solved"/>
+ </state>
+ <transition event="restart">
+ <script>
+ restart();
+ </script>
+ <raise event="update"/>
+ </transition>
+ <transition event="update">
+ <send event="updateGUI">
+ <param name="currentState" expr="currentState"/>
+ <param name="initState" expr="initState"/>
+ </send>
+ </transition>
+ </state>
+</scxml>
diff --git a/examples/scxml/trafficlight-common/Lights.ui.qml b/examples/scxml/trafficlight-common/Lights.ui.qml
new file mode 100644
index 0000000..a385eae
--- /dev/null
+++ b/examples/scxml/trafficlight-common/Lights.ui.qml
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+import QtQuick 2.5
+import QtQuick.Window 2.2
+import TrafficLightStateMachine 1.0
+
+Image {
+ id: lights
+
+ property alias button: button
+ property TrafficLightStateMachine stateMachine
+
+ source: "background.png"
+
+ Column {
+ y: 40
+ spacing: 27
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Image {
+ id: redLight
+ opacity: 0.2
+ source: "red.png"
+ }
+
+ Image {
+ id: yellowLight
+ opacity: 0.2
+ source: "yellow.png"
+ }
+
+ Image {
+ id: greenLight
+ opacity: 0.2
+ source: "green.png"
+ }
+ }
+
+ Button {
+ id: button
+
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ source: "pause.png"
+ }
+
+ states: [
+ State {
+ name: "Red"
+ when: stateMachine.red
+
+ PropertyChanges {
+ target: redLight
+ opacity: 1
+ }
+ },
+ State {
+ name: "RedGoingGreen"
+ when: stateMachine.redGoingGreen
+
+ PropertyChanges {
+ target: redLight
+ opacity: 1
+ }
+
+ PropertyChanges {
+ target: yellowLight
+ opacity: 1
+ }
+ },
+ State {
+ name: "Yellow"
+ when: stateMachine.yellow || stateMachine.blinking
+
+ PropertyChanges {
+ target: yellowLight
+ opacity: 1
+ }
+ },
+ State {
+ name: "Green"
+ when: stateMachine.green
+
+ PropertyChanges {
+ target: greenLight
+ opacity: 1
+ }
+ }
+ ]
+}
diff --git a/examples/scxml/trafficlight-common/TrafficLight.qml b/examples/scxml/trafficlight-common/TrafficLight.qml
index a23fca7..1da3726 100644
--- a/examples/scxml/trafficlight-common/TrafficLight.qml
+++ b/examples/scxml/trafficlight-common/TrafficLight.qml
@@ -50,65 +50,23 @@
import QtQuick 2.5
import QtQuick.Window 2.2
+import TrafficLightStateMachine 1.0
Window {
id: root
- property QtObject stateMachine
+
+ property TrafficLightStateMachine stateMachine
visible: true
- color: "black"
width: lights.width
height: lights.height
- Image {
+ Lights {
id: lights
- source: "background.png"
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- Qt.quit();
- }
- }
-
- Image {
- id: redLight
- x: (lights.width - width) / 2
- y: 40
- source: "red.png"
- visible: stateMachine.red || stateMachine.redGoingGreen
- }
-
- Image {
- id: yellowLight
- x: (lights.width - width) / 2
- y: 135
- source: "yellow.png"
- visible: stateMachine.yellow || stateMachine.blinking
- }
-
- Image {
- id: greenLight
- x: (lights.width - width) / 2
- y: 230
- source: "green.png"
- visible: stateMachine.green
- }
- }
-
- Button {
- id: button
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: 20
- source: stateMachine.working ? "pause.png" : "play.png"
+ stateMachine: root.stateMachine
+ button.source: stateMachine.working ? "pause.png" : "play.png"
- onClicked: {
- if (stateMachine.working)
- stateMachine.smash()
- else
- stateMachine.repair()
- }
+ button.onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
}
}
diff --git a/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc b/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc
index fb00130..ac16203 100644
--- a/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc
+++ b/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc
@@ -12,8 +12,6 @@
\skipto STATECHARTS
\printline scxml
- We also tell qmake to run \c qscxmlc, which generates \e statemachine.h and
- \e statemachine.cpp, and adds them to the \c HEADERS and \c SOURCES
- variables for compilation:
-
- \printuntil qscxmlc
+ The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
+ \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
+ and \c SOURCES variables for compilation.
diff --git a/examples/scxml/trafficlight-common/statemachine.scxml b/examples/scxml/trafficlight-common/statemachine.scxml
index 4224e81..3cf023e 100644
--- a/examples/scxml/trafficlight-common/statemachine.scxml
+++ b/examples/scxml/trafficlight-common/statemachine.scxml
@@ -50,7 +50,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
xmlns:qt="http://theqtcompany.com/scxml/2015/06/"
diff --git a/examples/scxml/trafficlight-common/trafficlight.cpp b/examples/scxml/trafficlight-common/trafficlight.cpp
index c14d4dc..66ed289 100644
--- a/examples/scxml/trafficlight-common/trafficlight.cpp
+++ b/examples/scxml/trafficlight-common/trafficlight.cpp
@@ -103,15 +103,15 @@ TrafficLight::TrafficLight(QScxmlStateMachine *machine, QWidget *parent)
setFixedSize(widget->sizeHint());
machine->connectToState(QStringLiteral("red"),
- widget->redLight(), SLOT(switchLight(bool)));
+ widget->redLight(), &LightWidget::switchLight);
machine->connectToState(QStringLiteral("redGoingGreen"),
- widget->redLight(), SLOT(switchLight(bool)));
+ widget->redLight(), &LightWidget::switchLight);
machine->connectToState(QStringLiteral("yellow"),
- widget->yellowLight(), SLOT(switchLight(bool)));
+ widget->yellowLight(), &LightWidget::switchLight);
machine->connectToState(QStringLiteral("blinking"),
- widget->yellowLight(), SLOT(switchLight(bool)));
+ widget->yellowLight(), &LightWidget::switchLight);
machine->connectToState(QStringLiteral("green"),
- widget->greenLight(), SLOT(switchLight(bool)));
+ widget->greenLight(), &LightWidget::switchLight);
QAbstractButton *button = new ButtonWidget(this);
auto setButtonGeometry = [this, button](){
diff --git a/examples/scxml/trafficlight-common/trafficlight.h b/examples/scxml/trafficlight-common/trafficlight.h
index cf9e22f..f64feda 100644
--- a/examples/scxml/trafficlight-common/trafficlight.h
+++ b/examples/scxml/trafficlight-common/trafficlight.h
@@ -63,7 +63,7 @@ class TrafficLight : public QWidget
public:
TrafficLight(QScxmlStateMachine *machine, QWidget *parent = 0);
-public slots:
+private slots:
void toggleWorking(bool pause);
private:
diff --git a/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc b/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc
index ecbf090..0316508 100644
--- a/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc
+++ b/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc
@@ -61,8 +61,7 @@
We connect to the states as follows:
- \quotefromfile trafficlight-common/TrafficLight.qml
- \skipto /^ {8}Image/
- \printuntil greenLight
- \printuntil /^ {8}\}/
+ \quotefromfile trafficlight-common/Lights.ui.qml
+ \skipto states
+ \printuntil ]
*/
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp
index b862386..a8bf00e 100644
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp
+++ b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp
@@ -51,11 +51,20 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
+#include <QScxmlStateMachine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
+ /* Register QScxmlStateMachine as TrafficLightStateMachine. This is required to have a type
+ * for the state machine and allows full code completion in the static case, since we
+ * share the QML code. */
+ qmlRegisterUncreatableType<QScxmlStateMachine>("TrafficLightStateMachine",
+ 1, 0,
+ "TrafficLightStateMachine",
+ QLatin1String("TrafficLightStateMachine is not creatable."));
+
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///trafficlight-qml-dynamic.qml")));
if (engine.rootObjects().isEmpty())
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
index 886424e..c03a7ef 100644
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
+++ b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
@@ -6,7 +6,5 @@ SOURCES += trafficlight-qml-dynamic.cpp
RESOURCES += trafficlight-qml-dynamic.qrc
-load(qscxmlc)
-
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-qml-dynamic
INSTALLS += target
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml
index 772d2fb..aef249a 100644
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml
+++ b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml
@@ -48,12 +48,12 @@
**
****************************************************************************/
-import QtScxml 5.7 as Scxml
+import QtScxml 5.8
TrafficLight {
- Scxml.StateMachineLoader {
+ StateMachineLoader {
id: loader
- filename: "qrc:///statemachine.scxml"
+ source: "qrc:///statemachine.scxml"
}
stateMachine: loader.stateMachine
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc
index 475a7be..547935b 100644
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc
+++ b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc
@@ -2,6 +2,7 @@
<qresource prefix="/">
<file alias="TrafficLight.qml">../trafficlight-common/TrafficLight.qml</file>
<file alias="Button.qml">../trafficlight-common/Button.qml</file>
+ <file alias="Lights.ui.qml">../trafficlight-common/Lights.ui.qml</file>
<file>trafficlight-qml-dynamic.qml</file>
<file alias="statemachine.scxml">../trafficlight-common/statemachine.scxml</file>
<file alias="green.png">../trafficlight-common/green.png</file>
diff --git a/tests/auto/qscxmlc/data/noContentInInvoke3.scxml b/examples/scxml/trafficlight-qml-simple/Light.qml
index 039f22b..8502d35 100644
--- a/tests/auto/qscxmlc/data/noContentInInvoke3.scxml
+++ b/examples/scxml/trafficlight-qml-simple/Light.qml
Binary files differ
diff --git a/tests/auto/qscxmlc/data/badInitial.scxml b/examples/scxml/trafficlight-qml-simple/TrafficLight.qml
index 52dffce..da51ded 100644
--- a/tests/auto/qscxmlc/data/badInitial.scxml
+++ b/examples/scxml/trafficlight-qml-simple/TrafficLight.qml
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,45 +47,62 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="MediaPlayerStateMachine"
- initial="stopped"
- datamodel="ecmascript"
->
- <datamodel>
- <data id="media"/>
- </datamodel>
- <script>
- function isValidMedia() {
- var m = _event.data.media
- return (m + "").length > 0
+import QtQuick 2.5
+import QtQuick.Window 2.2
+import TrafficLightStateMachine 1.0
+
+Window {
+ id: root
+ visible: true
+ width: 100
+ height: 350
+
+ TrafficLightStateMachine {
+ id: stateMachine
+ running: true
+ }
+
+ Item {
+ id: lights
+ width: parent.width
+ height: 300
+
+ Light {
+ anchors.top: parent.top
+ color: "red"
+ visible: stateMachine.red || stateMachine.redGoingGreen
}
- <initial>
- <state id="stopped">
- <transition event="tap" cond="isValidMedia()" target="playing"/>
- </state>
+ Light {
+ anchors.centerIn: parent
+ color: "yellow"
+ visible: stateMachine.yellow || stateMachine.blinking
+ }
- <state id="playing">
- <onentry>
- <assign location="media" expr="_event.data.media"/>
- <send type="qt:signal" event="playbackStarted">
- <param name="media" expr="media"/>
- </send>
- </onentry>
+ Light {
+ anchors.bottom: parent.bottom
+ color: "green"
+ visible: stateMachine.green
+ }
+ }
+
+ Rectangle {
+ anchors.top: lights.bottom
+ anchors.bottom: parent.bottom
+ width: parent.width
+ border.color: "black"
- <onexit>
- <send type="qt:signal" event="playbackStopped">
- <param name="media" expr="media"/>
- </send>
- </onexit>
+ Text {
+ anchors.fill: parent
+ text: stateMachine.working ? "Pause" : "Unpause"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
- <transition event="tap" cond="!isValidMedia() || media === _event.data.media" target="stopped"/>
- <transition event="tap" cond="isValidMedia() &amp;&amp; media !== _event.data.media" target="playing"/>
- </state>
-</scxml>
+ MouseArea {
+ anchors.fill: parent
+ onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
+ }
+ }
+}
diff --git a/examples/scxml/trafficlight-qml-simple/doc/images/trafficlight.png b/examples/scxml/trafficlight-qml-simple/doc/images/trafficlight.png
new file mode 100644
index 0000000..ddd7431
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/doc/images/trafficlight.png
Binary files differ
diff --git a/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc b/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc
new file mode 100644
index 0000000..f66f5f7
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example trafficlight-qml-simple
+ \title Qt SCXML Traffic Light QML Example (Simple)
+ \ingroup examples-qtscxml
+
+ \brief A Qt Quick application that uses a compiled state machine to
+ implement a simplified traffic light.
+
+ \image trafficlight.png
+
+ \e{Traffic Light QML Example (Simple)} demonstrates how to connect to the
+ active properties of a state in a state machine that is compiled to a class.
+
+ The UI is created using Qt Quick.
+
+ \include examples-run.qdocinc
+
+ \include trafficlight-compiling.qdocinc
+
+ \section1 Instantiating the State Machine
+
+ We instantiate the state machine as follows:
+
+ \quotefromfile trafficlight-qml-simple/TrafficLight.qml
+
+ \skipto TrafficLightStateMachine {
+ \printuntil }
+
+ \include trafficlight-state-machine.qdocinc
+
+ We connect to the states as follows:
+
+ \quotefromfile trafficlight-qml-simple/TrafficLight.qml
+ \skipto Light {
+ \printuntil /^ {4}\}/
+*/
diff --git a/tests/auto/qscxmlc/data/noContentInInvoke2.scxml b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp
index 8d7f017..c9950c4 100644
--- a/tests/auto/qscxmlc/data/noContentInInvoke2.scxml
+++ b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -49,31 +47,25 @@
** $QT_END_LICENSE$
**
****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyp\ace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <contenon use t <sl name="anywhere version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- * Redistr </content>
- </invoke>
- , </state>
- </state>
-</scxml>
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QQmlContext>
+
+#include "statemachine.h"
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<TrafficLightStateMachine>("TrafficLightStateMachine", 1, 0,
+ "TrafficLightStateMachine");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/TrafficLight.qml")));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
+
diff --git a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro
new file mode 100644
index 0000000..4de04ea
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += qml scxml
+CONFIG += c++11
+
+SOURCES += trafficlight-qml-simple.cpp
+
+RESOURCES += trafficlight-qml-simple.qrc
+
+STATECHARTS = ../trafficlight-common/statemachine.scxml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-qml-simple
+INSTALLS += target
+
diff --git a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc
new file mode 100644
index 0000000..bb75dba
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>TrafficLight.qml</file>
+ <file>Light.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc b/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc
index a83de5f..814f410 100644
--- a/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc
+++ b/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc
@@ -57,8 +57,7 @@
We connect to the states as follows:
- \quotefromfile trafficlight-common/TrafficLight.qml
- \skipto /^ {8}Image/
- \printuntil greenLight
- \printuntil /^ {8}\}/
+ \quotefromfile trafficlight-common/Lights.ui.qml
+ \skipto states
+ \printuntil ]
*/
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro
index 4c19594..e5dd64c 100644
--- a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro
+++ b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro
@@ -9,8 +9,6 @@ RESOURCES += trafficlight-qml-static.qrc
STATECHARTS = ../trafficlight-common/statemachine.scxml
-load(qscxmlc)
-
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-qml-static
INSTALLS += target
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc
index 46c408f..890b4a7 100644
--- a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc
+++ b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc
@@ -3,6 +3,7 @@
<file>trafficlight-qml-static.qml</file>
<file alias="TrafficLight.qml">../trafficlight-common/TrafficLight.qml</file>
<file alias="Button.qml">../trafficlight-common/Button.qml</file>
+ <file alias="Lights.ui.qml">../trafficlight-common/Lights.ui.qml</file>
<file alias="green.png">../trafficlight-common/green.png</file>
<file alias="yellow.png">../trafficlight-common/yellow.png</file>
<file alias="red.png">../trafficlight-common/red.png</file>
diff --git a/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp b/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp
index cc0044d..7b0489e 100644
--- a/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp
+++ b/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp
@@ -51,7 +51,6 @@
#include "../trafficlight-common/trafficlight.h"
#include <QApplication>
-#include <QScxmlNullDataModel>
#include <QTextStream>
int main(int argc, char **argv)
@@ -61,7 +60,8 @@ int main(int argc, char **argv)
QScxmlStateMachine *machine = QScxmlStateMachine::fromFile(QStringLiteral(":statemachine.scxml"));
if (!machine->parseErrors().isEmpty()) {
QTextStream errs(stderr, QIODevice::WriteOnly);
- foreach (const QScxmlError &error, machine->parseErrors()) {
+ const auto errors = machine->parseErrors();
+ for (const QScxmlError &error : errors) {
errs << error.toString();
}
diff --git a/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.pro b/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.pro
index 21da884..5613249 100644
--- a/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.pro
+++ b/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.pro
@@ -9,7 +9,5 @@ SOURCES += trafficlight-widgets-dynamic.cpp
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-widgets-dynamic
INSTALLS += target
-load(qscxmlc)
-
RESOURCES += \
trafficlight-widgets-dynamic.qrc
diff --git a/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.pro b/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.pro
index c98fa98..8a32984 100644
--- a/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.pro
+++ b/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.pro
@@ -5,8 +5,6 @@ SOURCES = ../trafficlight-common/trafficlight.cpp
HEADERS = ../trafficlight-common/trafficlight.h
STATECHARTS = ../trafficlight-common/statemachine.scxml
-load(qscxmlc)
-
SOURCES += trafficlight-widgets-static.cpp
# install
diff --git a/mkspecs/features/qscxmlc.prf b/mkspecs/features/qscxmlc.prf
index 4631377..25f1f10 100644
--- a/mkspecs/features/qscxmlc.prf
+++ b/mkspecs/features/qscxmlc.prf
@@ -9,13 +9,12 @@ debug_and_release {
QSCXMLC_DIR = $$QSCXMLC_DIR$$SUFFIX
-msvc:lessThan(MSC_VER,1800):QMAKE_QSCXMLC=$$QMAKE_QSCXMLC --no-c++11 # VS2012 cannot handle initializer lists.
-
{
qscxmlc.name = QSCXMLC ${QMAKE_FILE_IN}.h
qscxmlc.input = STATECHARTS
qscxmlc.variable_out = QSCXMLC_HEADERS
qscxmlc.commands = $$QMAKE_QSCXMLC ${QMAKE_FILE_IN} --header ${QMAKE_FILE_OUT} --impl $$QSCXMLC_DIR/${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}
+!isEmpty(QSCXMLC_NAMESPACE): qscxmlc.commands = $${qscxmlc.commands} --namespace $$QSCXMLC_NAMESPACE
qscxmlc.output = $$QSCXMLC_DIR/${QMAKE_FILE_BASE}$${first(QMAKE_EXT_H)}
qscxmlc.CONFIG += target_predeps
qscxmlc.depends += $$QMAKE_QSCXMLC_EXE
diff --git a/src/imports/scxmlstatemachine/eventconnection.cpp b/src/imports/scxmlstatemachine/eventconnection.cpp
new file mode 100644
index 0000000..9803a9a
--- /dev/null
+++ b/src/imports/scxmlstatemachine/eventconnection.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "eventconnection_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QScxmlEventConnection::QScxmlEventConnection(QObject *parent) :
+ QObject(parent), m_stateMachine(nullptr)
+{
+}
+
+QStringList QScxmlEventConnection::events() const
+{
+ return m_events;
+}
+
+void QScxmlEventConnection::setEvents(const QStringList &events)
+{
+ if (events != m_events) {
+ m_events = events;
+ doConnect();
+ emit eventsChanged();
+ }
+}
+
+QScxmlStateMachine *QScxmlEventConnection::stateMachine() const
+{
+ return m_stateMachine;
+}
+
+void QScxmlEventConnection::setStateMachine(QScxmlStateMachine *stateMachine)
+{
+ if (stateMachine != m_stateMachine) {
+ m_stateMachine = stateMachine;
+ doConnect();
+ emit stateMachineChanged();
+ }
+}
+
+void QScxmlEventConnection::doConnect()
+{
+ for (const QMetaObject::Connection &connection : qAsConst(m_connections))
+ disconnect(connection);
+ m_connections.clear();
+ if (m_stateMachine) {
+ for (const QString &event : qAsConst(m_events)) {
+ m_connections.append(m_stateMachine->connectToEvent(event, this,
+ &QScxmlEventConnection::occurred));
+ }
+
+ }
+
+}
+
+void QScxmlEventConnection::classBegin()
+{
+}
+
+void QScxmlEventConnection::componentComplete()
+{
+ if (!m_stateMachine) {
+ if ((m_stateMachine = qobject_cast<QScxmlStateMachine *>(parent())))
+ doConnect();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/scxmlstatemachine/eventconnection_p.h b/src/imports/scxmlstatemachine/eventconnection_p.h
new file mode 100644
index 0000000..5fea645
--- /dev/null
+++ b/src/imports/scxmlstatemachine/eventconnection_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EVENTCONNECTION_P_H
+#define EVENTCONNECTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScxml/qscxmlstatemachine.h>
+#include <QtCore/qobject.h>
+#include <QtQml/qqmlparserstatus.h>
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlEventConnection : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QStringList events READ events WRITE setEvents NOTIFY eventsChanged)
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
+ NOTIFY stateMachineChanged)
+ Q_INTERFACES(QQmlParserStatus)
+
+public:
+ QScxmlEventConnection(QObject *parent = nullptr);
+
+ QStringList events() const;
+ void setEvents(const QStringList &events);
+
+ QScxmlStateMachine *stateMachine() const;
+ void setStateMachine(QScxmlStateMachine *stateMachine);
+
+Q_SIGNALS:
+ void eventsChanged();
+ void stateMachineChanged();
+
+ void occurred(const QScxmlEvent &event);
+
+private:
+ QScxmlStateMachine *m_stateMachine;
+ QStringList m_events;
+
+ QList<QMetaObject::Connection> m_connections;
+
+ void doConnect();
+ void classBegin() override;
+ void componentComplete() override;
+};
+
+QT_END_NAMESPACE
+
+#endif // EVENTCONNECTION_P_H
diff --git a/src/imports/scxmlstatemachine/invokedservices.cpp b/src/imports/scxmlstatemachine/invokedservices.cpp
new file mode 100644
index 0000000..93c97d6
--- /dev/null
+++ b/src/imports/scxmlstatemachine/invokedservices.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "invokedservices_p.h"
+#include <QtScxml/qscxmlinvokableservice.h>
+
+QT_BEGIN_NAMESPACE
+
+QScxmlInvokedServices::QScxmlInvokedServices(QObject *parent) : QObject(parent)
+{
+}
+
+QVariantMap QScxmlInvokedServices::children()
+{
+ QVariantMap ret;
+ if (m_stateMachine) {
+ const QVector<QScxmlInvokableService *> children = m_stateMachine->invokedServices();
+ for (QScxmlInvokableService *service : children)
+ ret.insertMulti(service->name(), QVariant::fromValue(service));
+ }
+ return ret;
+}
+
+void QScxmlInvokedServices::classBegin()
+{
+}
+
+QScxmlStateMachine *QScxmlInvokedServices::stateMachine() const
+{
+ return m_stateMachine;
+}
+
+void QScxmlInvokedServices::setStateMachine(QScxmlStateMachine *stateMachine)
+{
+ if (stateMachine != m_stateMachine) {
+ if (m_stateMachine) {
+ disconnect(m_stateMachine, &QScxmlStateMachine::invokedServicesChanged,
+ this, &QScxmlInvokedServices::childrenChanged);
+ }
+ m_stateMachine = stateMachine;
+ connect(m_stateMachine, &QScxmlStateMachine::invokedServicesChanged,
+ this, &QScxmlInvokedServices::childrenChanged);
+ emit stateMachineChanged();
+ emit childrenChanged();
+ }
+}
+
+QQmlListProperty<QObject> QScxmlInvokedServices::qmlChildren()
+{
+ return QQmlListProperty<QObject>(this, m_qmlChildren);
+}
+
+
+void QScxmlInvokedServices::componentComplete()
+{
+ if (!m_stateMachine) {
+ if ((m_stateMachine = qobject_cast<QScxmlStateMachine *>(parent()))) {
+ connect(m_stateMachine, &QScxmlStateMachine::invokedServicesChanged,
+ this, &QScxmlInvokedServices::childrenChanged);
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/scxmlstatemachine/invokedservices_p.h b/src/imports/scxmlstatemachine/invokedservices_p.h
new file mode 100644
index 0000000..72d6f2a
--- /dev/null
+++ b/src/imports/scxmlstatemachine/invokedservices_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INVOKEDSERVICES_P_H
+#define INVOKEDSERVICES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qqmllist.h>
+#include <QtScxml/qscxmlstatemachine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlInvokedServices : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
+ NOTIFY stateMachineChanged)
+ Q_PROPERTY(QVariantMap children READ children NOTIFY childrenChanged)
+ Q_PROPERTY(QQmlListProperty<QObject> qmlChildren READ qmlChildren)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_CLASSINFO("DefaultProperty", "qmlChildren")
+public:
+ QScxmlInvokedServices(QObject *parent = 0);
+ QVariantMap children();
+
+ QScxmlStateMachine *stateMachine() const;
+ void setStateMachine(QScxmlStateMachine *stateMachine);
+
+ QQmlListProperty<QObject> qmlChildren();
+
+Q_SIGNALS:
+ void childrenChanged();
+ void stateMachineChanged();
+
+private:
+ void classBegin() override;
+ void componentComplete() override;
+
+ QScxmlStateMachine *m_stateMachine = 0;
+ QList<QObject *> m_qmlChildren;
+};
+
+QT_END_NAMESPACE
+
+#endif // INVOKEDSERVICES_P_H
diff --git a/src/imports/scxmlstatemachine/plugin.cpp b/src/imports/scxmlstatemachine/plugin.cpp
index 1837935..6842846 100644
--- a/src/imports/scxmlstatemachine/plugin.cpp
+++ b/src/imports/scxmlstatemachine/plugin.cpp
@@ -37,8 +37,11 @@
**
****************************************************************************/
-#include "statemachineloader.h"
+#include "statemachineloader_p.h"
+#include "eventconnection_p.h"
#include "qscxmlevent.h"
+#include "statemachineextended_p.h"
+#include "invokedservices_p.h"
#include <QQmlExtensionPlugin>
#include <qqml.h>
@@ -57,7 +60,7 @@ public:
Q_ASSERT(uri == QStringLiteral("QtScxml"));
int major = 5;
- int minor = 7;
+ int minor = 8;
// Do not rely on RegisterMethodArgumentMetaType meta-call to register the QScxmlEvent type.
// This registration is required for the receiving end of the signal emission that carries
// parameters of this type to be able to treat them correctly as a gadget. This is because the
@@ -66,6 +69,10 @@ public:
static const int qScxmlEventMetaTypeId = qMetaTypeId<QScxmlEvent>();
Q_UNUSED(qScxmlEventMetaTypeId)
qmlRegisterType<QScxmlStateMachineLoader>(uri, major, minor, "StateMachineLoader");
+ qmlRegisterType<QScxmlEventConnection>(uri, major, minor, "EventConnection");
+ qmlRegisterType<QScxmlInvokedServices>(uri, major, minor, "InvokedServices");
+ qmlRegisterExtendedUncreatableType<QScxmlStateMachine, QScxmlStateMachineExtended>(
+ uri, major, minor, "StateMachine", "Only created through derived types");
qmlProtectModule(uri, 1);
}
};
diff --git a/src/imports/scxmlstatemachine/plugins.qmltypes b/src/imports/scxmlstatemachine/plugins.qmltypes
index 1366717..a7a72e7 100644
--- a/src/imports/scxmlstatemachine/plugins.qmltypes
+++ b/src/imports/scxmlstatemachine/plugins.qmltypes
@@ -4,16 +4,134 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtScxml 5.7'
+// 'qmlplugindump -nonrelocatable QtScxml 5.8'
Module {
- dependencies: ["QtQuick 2.0"]
+ dependencies: ["QtQuick 2.8"]
+ Component {
+ name: "QScxmlEventConnection"
+ prototype: "QObject"
+ exports: ["QtScxml/EventConnection 5.8"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "events"; type: "QStringList" }
+ Property { name: "stateMachine"; type: "QScxmlStateMachine"; isPointer: true }
+ Signal {
+ name: "occurred"
+ Parameter { name: "event"; type: "QScxmlEvent" }
+ }
+ }
+ Component {
+ name: "QScxmlInvokedServices"
+ defaultProperty: "qmlChildren"
+ prototype: "QObject"
+ exports: ["QtScxml/InvokedServices 5.8"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "stateMachine"; type: "QScxmlStateMachine"; isPointer: true }
+ Property { name: "children"; type: "QVariantMap"; isReadonly: true }
+ Property { name: "qmlChildren"; type: "QObject"; isList: true; isReadonly: true }
+ }
+ Component {
+ name: "QScxmlStateMachine"
+ prototype: "QObject"
+ Property { name: "running"; type: "bool" }
+ Property { name: "initialized"; type: "bool"; isReadonly: true }
+ Property { name: "dataModel"; type: "QScxmlDataModel"; isPointer: true }
+ Property { name: "initialValues"; type: "QVariantMap" }
+ Property { name: "invokedServices"; type: "QVector<QScxmlInvokableService*>"; isReadonly: true }
+ Property { name: "sessionId"; type: "string"; isReadonly: true }
+ Property { name: "name"; type: "string"; isReadonly: true }
+ Property { name: "invoked"; type: "bool"; isReadonly: true }
+ Property { name: "parseErrors"; type: "QVector<QScxmlError>"; isReadonly: true }
+ Property { name: "loader"; type: "QScxmlParser::Loader"; isPointer: true }
+ Signal {
+ name: "runningChanged"
+ Parameter { name: "running"; type: "bool" }
+ }
+ Signal {
+ name: "invokedServicesChanged"
+ Parameter { name: "invokedServices"; type: "QVector<QScxmlInvokableService*>" }
+ }
+ Signal {
+ name: "log"
+ Parameter { name: "label"; type: "string" }
+ Parameter { name: "msg"; type: "string" }
+ }
+ Signal { name: "reachedStableState" }
+ Signal { name: "finished" }
+ Signal {
+ name: "dataModelChanged"
+ Parameter { name: "model"; type: "QScxmlDataModel"; isPointer: true }
+ }
+ Signal {
+ name: "initialValuesChanged"
+ Parameter { name: "initialValues"; type: "QVariantMap" }
+ }
+ Signal {
+ name: "initializedChanged"
+ Parameter { name: "initialized"; type: "bool" }
+ }
+ Signal {
+ name: "loaderChanged"
+ Parameter { name: "loader"; type: "QScxmlParser::Loader"; isPointer: true }
+ }
+ Method { name: "start" }
+ Method { name: "stop" }
+ Method { name: "init"; type: "bool" }
+ Method {
+ name: "stateNames"
+ type: "QStringList"
+ Parameter { name: "compress"; type: "bool" }
+ }
+ Method { name: "stateNames"; type: "QStringList" }
+ Method {
+ name: "activeStateNames"
+ type: "QStringList"
+ Parameter { name: "compress"; type: "bool" }
+ }
+ Method { name: "activeStateNames"; type: "QStringList" }
+ Method {
+ name: "isActive"
+ type: "bool"
+ Parameter { name: "scxmlStateName"; type: "string" }
+ }
+ Method {
+ name: "submitEvent"
+ Parameter { name: "event"; type: "QScxmlEvent"; isPointer: true }
+ }
+ Method {
+ name: "submitEvent"
+ Parameter { name: "eventName"; type: "string" }
+ }
+ Method {
+ name: "submitEvent"
+ Parameter { name: "eventName"; type: "string" }
+ Parameter { name: "data"; type: "QVariant" }
+ }
+ Method {
+ name: "cancelDelayedEvent"
+ Parameter { name: "sendId"; type: "string" }
+ }
+ Method {
+ name: "isDispatchableTarget"
+ type: "bool"
+ Parameter { name: "target"; type: "string" }
+ }
+ }
+ Component {
+ name: "QScxmlStateMachineExtended"
+ defaultProperty: "children"
+ prototype: "QScxmlStateMachine"
+ exports: ["QtScxml/StateMachine 5.8"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Property { name: "children"; type: "QObject"; isList: true; isReadonly: true }
+ }
Component {
name: "QScxmlStateMachineLoader"
prototype: "QObject"
- exports: ["QtScxml/StateMachineLoader 5.7"]
+ exports: ["QtScxml/StateMachineLoader 5.8"]
exportMetaObjectRevisions: [0]
- Property { name: "filename"; type: "QUrl" }
+ Property { name: "source"; type: "QUrl" }
Property { name: "stateMachine"; type: "QScxmlStateMachine"; isReadonly: true; isPointer: true }
Property { name: "initialValues"; type: "QVariantMap" }
Property { name: "dataModel"; type: "QScxmlDataModel"; isPointer: true }
diff --git a/src/imports/scxmlstatemachine/scxmlstatemachine.pro b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
index 23497ac..a45a5f3 100644
--- a/src/imports/scxmlstatemachine/scxmlstatemachine.pro
+++ b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
@@ -5,10 +5,17 @@ QT = scxml qml-private core-private
SOURCES = \
$$PWD/plugin.cpp \
- $$PWD/statemachineloader.cpp
+ $$PWD/statemachineloader.cpp \
+ $$PWD/eventconnection.cpp \
+ $$PWD/statemachineextended.cpp \
+ $$PWD/invokedservices.cpp
HEADERS = \
- $$PWD/statemachineloader.h
+ $$PWD/eventconnection_p.h \
+ $$PWD/invokedservices_p.h \
+ $$PWD/statemachineextended_p.h \
+ $$PWD/statemachineloader_p.h
+
load(qml_plugin)
diff --git a/src/imports/scxmlstatemachine/statemachineextended.cpp b/src/imports/scxmlstatemachine/statemachineextended.cpp
new file mode 100644
index 0000000..6b1b13b
--- /dev/null
+++ b/src/imports/scxmlstatemachine/statemachineextended.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "statemachineextended_p.h"
+
+#include <QtScxml/qscxmlglobals.h>
+#include <QtScxml/qscxmlstatemachine.h>
+
+QT_BEGIN_NAMESPACE
+
+QScxmlStateMachineExtended::QScxmlStateMachineExtended(QObject *extendee) :
+ QObject(extendee)
+{
+}
+
+QQmlListProperty<QObject> QScxmlStateMachineExtended::children()
+{
+ return QQmlListProperty<QObject>(this, m_children);
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/scxmlstatemachine/statemachineextended_p.h b/src/imports/scxmlstatemachine/statemachineextended_p.h
new file mode 100644
index 0000000..ab069b1
--- /dev/null
+++ b/src/imports/scxmlstatemachine/statemachineextended_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef STATEMACHINEEXTENDED_P_H
+#define STATEMACHINEEXTENDED_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScxml/qscxmlglobals.h>
+#include <QtCore/qobject.h>
+#include <QtQml/qqmllist.h>
+
+QT_BEGIN_NAMESPACE
+
+/* Allow State Machines created from QML to have children. */
+class QScxmlStateMachineExtended : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QObject> children READ children)
+ Q_CLASSINFO("DefaultProperty", "children")
+public:
+ QScxmlStateMachineExtended(QObject *extendee);
+ QQmlListProperty<QObject> children();
+
+private:
+ QObjectList m_children;
+};
+
+QT_END_NAMESPACE
+
+#endif // STATEMACHINEEXTENDED_P_H
diff --git a/src/imports/scxmlstatemachine/statemachineloader.cpp b/src/imports/scxmlstatemachine/statemachineloader.cpp
index 68bdae8..cc908c6 100644
--- a/src/imports/scxmlstatemachine/statemachineloader.cpp
+++ b/src/imports/scxmlstatemachine/statemachineloader.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "statemachineloader.h"
+#include "statemachineloader_p.h"
#include <QtScxml/qscxmlstatemachine.h>
#include <QQmlContext>
@@ -49,20 +49,23 @@
/*!
\qmltype StateMachineLoader
\instantiates QScxmlStateMachineLoader
- \inqmlmodule Scxml
+ \inqmlmodule QtScxml
- \brief Dynamically loads an SCXML file and instantiates the state machine.
+ \brief Dynamically loads an SCXML document and instantiates the state machine.
\since QtScxml 5.7
*/
/*!
- \qmlsignal StateMachineLoader::filenameChanged()
- This signal is emitted when the user changes the filename.
+ \qmlsignal StateMachineLoader::sourceChanged()
+ This signal is emitted when the user changes the source URL for the SCXML document.
*/
/*!
\qmlsignal StateMachineLoader::stateMachineChanged()
+
+ This signal is emitted when the stateMachine property changes. That is, when
+ a new state machine is loaded or when the old one becomes invalid.
*/
QScxmlStateMachineLoader::QScxmlStateMachineLoader(QObject *parent)
@@ -84,34 +87,34 @@ QT_PREPEND_NAMESPACE(QScxmlStateMachine) *QScxmlStateMachineLoader::stateMachine
}
/*!
- \qmlproperty string StateMachineLoader::filename
+ \qmlproperty string StateMachineLoader::source
- The name of the SCXML file to load.
+ The url of the SCXML document to load. Only synchronously accessible URLs are supported.
*/
-QUrl QScxmlStateMachineLoader::filename()
+QUrl QScxmlStateMachineLoader::source()
{
- return m_filename;
+ return m_source;
}
-void QScxmlStateMachineLoader::setFilename(const QUrl &filename)
+void QScxmlStateMachineLoader::setSource(const QUrl &source)
{
- if (!filename.isValid())
+ if (!source.isValid())
return;
- QUrl oldFilename = m_filename;
+ QUrl oldSource = m_source;
if (m_stateMachine) {
delete m_stateMachine;
m_stateMachine = Q_NULLPTR;
m_implicitDataModel = Q_NULLPTR;
}
- if (parse(filename)) {
- m_filename = filename;
- emit filenameChanged();
+ if (parse(source)) {
+ m_source = source;
+ emit sourceChanged();
} else {
- m_filename.clear();
- if (!oldFilename.isEmpty()) {
- emit filenameChanged();
+ m_source.clear();
+ if (!oldSource.isEmpty()) {
+ emit sourceChanged();
}
}
}
@@ -150,16 +153,17 @@ void QScxmlStateMachineLoader::setDataModel(QScxmlDataModel *dataModel)
}
}
-bool QScxmlStateMachineLoader::parse(const QUrl &filename)
+bool QScxmlStateMachineLoader::parse(const QUrl &source)
{
- if (!QQmlFile::isSynchronous(filename)) {
- qmlInfo(this) << QStringLiteral("ERROR: cannot open '%1' for reading: only synchronous file access is supported.").arg(filename.fileName());
+ if (!QQmlFile::isSynchronous(source)) {
+ qmlInfo(this) << QStringLiteral("ERROR: cannot open '%1' for reading: only synchronous access is supported.")
+ .arg(source.url());
return false;
}
- QQmlFile scxmlFile(QQmlEngine::contextForObject(this)->engine(), filename);
+ QQmlFile scxmlFile(QQmlEngine::contextForObject(this)->engine(), source);
if (scxmlFile.isError()) {
// the synchronous case can only fail when the file is not found (or not readable).
- qmlInfo(this) << QStringLiteral("ERROR: cannot open '%1' for reading.").arg(filename.fileName());
+ qmlInfo(this) << QStringLiteral("ERROR: cannot open '%1' for reading.").arg(source.url());
return false;
}
@@ -170,7 +174,7 @@ bool QScxmlStateMachineLoader::parse(const QUrl &filename)
return false;
}
- m_stateMachine = QScxmlStateMachine::fromData(&buf, filename.toString());
+ m_stateMachine = QScxmlStateMachine::fromData(&buf, source.toString());
m_stateMachine->setParent(this);
m_implicitDataModel = m_stateMachine->dataModel();
@@ -185,9 +189,12 @@ bool QScxmlStateMachineLoader::parse(const QUrl &filename)
QMetaObject::invokeMethod(m_stateMachine, "start", Qt::QueuedConnection);
return true;
} else {
- qmlInfo(this) << QStringLiteral("Something went wrong while parsing '%1':").arg(filename.fileName()) << endl;
- foreach (const QScxmlError &msg, m_stateMachine->parseErrors()) {
- qmlInfo(this) << msg.toString();
+ qmlInfo(this) << QStringLiteral("Something went wrong while parsing '%1':")
+ .arg(source.url())
+ << endl;
+ const auto errors = m_stateMachine->parseErrors();
+ for (const QScxmlError &error : errors) {
+ qmlInfo(this) << error.toString();
}
emit stateMachineChanged();
diff --git a/src/imports/scxmlstatemachine/statemachineloader.h b/src/imports/scxmlstatemachine/statemachineloader_p.h
index 0e73a50..6eaf6f4 100644
--- a/src/imports/scxmlstatemachine/statemachineloader.h
+++ b/src/imports/scxmlstatemachine/statemachineloader_p.h
@@ -37,8 +37,19 @@
**
****************************************************************************/
-#ifndef STATEMACHINELOADER_H
-#define STATEMACHINELOADER_H
+#ifndef STATEMACHINELOADER_P_H
+#define STATEMACHINELOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
#include <QUrl>
#include <QtScxml/qscxmlstatemachine.h>
@@ -49,7 +60,7 @@ QT_BEGIN_NAMESPACE
class QScxmlStateMachineLoader: public QObject
{
Q_OBJECT
- Q_PROPERTY(QUrl filename READ filename WRITE setFilename NOTIFY filenameChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QScxmlStateMachine* stateMachine READ stateMachine DESIGNABLE false NOTIFY stateMachineChanged)
Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues NOTIFY initialValuesChanged)
Q_PROPERTY(QScxmlDataModel* dataModel READ dataModel WRITE setDataModel NOTIFY dataModelChanged)
@@ -60,8 +71,8 @@ public:
QScxmlStateMachine *stateMachine() const;
- QUrl filename();
- void setFilename(const QUrl &filename);
+ QUrl source();
+ void setSource(const QUrl &source);
QVariantMap initialValues() const;
void setInitialValues(const QVariantMap &initialValues);
@@ -70,16 +81,16 @@ public:
void setDataModel(QScxmlDataModel *dataModel);
Q_SIGNALS:
- void filenameChanged();
+ void sourceChanged();
void initialValuesChanged();
void stateMachineChanged();
void dataModelChanged();
private:
- bool parse(const QUrl &filename);
+ bool parse(const QUrl &source);
private:
- QUrl m_filename;
+ QUrl m_source;
QVariantMap m_initialValues;
QScxmlDataModel *m_dataModel;
QScxmlDataModel *m_implicitDataModel;
@@ -88,4 +99,4 @@ private:
QT_END_NAMESPACE
-#endif // STATEMACHINELOADER_H
+#endif // STATEMACHINELOADER_P_H
diff --git a/src/scxml/Qt5ScxmlConfigExtras.cmake.in b/src/scxml/Qt5ScxmlConfigExtras.cmake.in
new file mode 100644
index 0000000..edb320a
--- /dev/null
+++ b/src/scxml/Qt5ScxmlConfigExtras.cmake.in
@@ -0,0 +1,51 @@
+#
+# Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+# Contact: https://www.qt.io/licensing/
+#
+# This file is part of the QtScxml module of the Qt Toolkit.
+#
+# $QT_BEGIN_LICENSE:LGPL$
+# Commercial License Usage
+# Licensees holding valid commercial Qt licenses may use this file in
+# accordance with the commercial license agreement provided with the
+# Software or, alternatively, in accordance with the terms contained in
+# a written agreement between you and The Qt Company. For licensing terms
+# and conditions see https://www.qt.io/terms-conditions. For further
+# information use the contact form at https://www.qt.io/contact-us.
+#
+# GNU Lesser General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU Lesser
+# General Public License version 3 as published by the Free Software
+# Foundation and appearing in the file LICENSE.LGPL3 included in the
+# packaging of this file. Please review the following information to
+# ensure the GNU Lesser General Public License version 3 requirements
+# will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+#
+# GNU General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU
+# General Public License version 2.0 or (at your option) the GNU General
+# Public license version 3 or any later version approved by the KDE Free
+# Qt Foundation. The licenses are as published by the Free Software
+# Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+# included in the packaging of this file. Please review the following
+# information to ensure the GNU General Public License requirements will
+# be met: https://www.gnu.org/licenses/gpl-2.0.html and
+# https://www.gnu.org/licenses/gpl-3.0.html.
+#
+# $QT_END_LICENSE$
+
+if (NOT TARGET Qt5::qscxmlc)
+ add_executable(Qt5::qscxmlc IMPORTED)
+
+!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE)
+ set(imported_location \"${_qt5Scxml_install_prefix}/$${CMAKE_BIN_DIR}qscxmlc$$CMAKE_BIN_SUFFIX\")
+!!ELSE
+ set(imported_location \"$${CMAKE_BIN_DIR}qscxmlc$$CMAKE_BIN_SUFFIX\")
+!!ENDIF
+ _qt5_Scxml_check_file_exists(${imported_location})
+
+ set_target_properties(Qt5::qscxmlc PROPERTIES
+ IMPORTED_LOCATION ${imported_location}
+ )
+ get_target_property(Qt5Scxml_QSCXMLC_EXECUTABLE Qt5::qscxmlc LOCATION)
+endif()
diff --git a/src/scxml/Qt5ScxmlMacros.cmake b/src/scxml/Qt5ScxmlMacros.cmake
new file mode 100644
index 0000000..234258c
--- /dev/null
+++ b/src/scxml/Qt5ScxmlMacros.cmake
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+# Contact: https://www.qt.io/licensing/
+#
+# This file is part of the QtScxml module of the Qt Toolkit.
+#
+# $QT_BEGIN_LICENSE:LGPL$
+# Commercial License Usage
+# Licensees holding valid commercial Qt licenses may use this file in
+# accordance with the commercial license agreement provided with the
+# Software or, alternatively, in accordance with the terms contained in
+# a written agreement between you and The Qt Company. For licensing terms
+# and conditions see https://www.qt.io/terms-conditions. For further
+# information use the contact form at https://www.qt.io/contact-us.
+#
+# GNU Lesser General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU Lesser
+# General Public License version 3 as published by the Free Software
+# Foundation and appearing in the file LICENSE.LGPL3 included in the
+# packaging of this file. Please review the following information to
+# ensure the GNU Lesser General Public License version 3 requirements
+# will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+#
+# GNU General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU
+# General Public License version 2.0 or (at your option) the GNU General
+# Public license version 3 or any later version approved by the KDE Free
+# Qt Foundation. The licenses are as published by the Free Software
+# Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+# included in the packaging of this file. Please review the following
+# information to ensure the GNU General Public License requirements will
+# be met: https://www.gnu.org/licenses/gpl-2.0.html and
+# https://www.gnu.org/licenses/gpl-3.0.html.
+#
+# $QT_END_LICENSE$
+
+if(NOT Qt5Scxml_QSCXMLC_EXECUTABLE)
+ message(FATAL_ERROR "qscxmlc executable not found -- Check installation.")
+endif()
+
+# qt5_add_statecharts(outfiles inputfile ... )
+
+function(qt5_add_statecharts outfiles)
+ set(options)
+ set(oneValueArgs)
+ set(multiValueArgs OPTIONS)
+
+ cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(scxml_files ${ARGS_UNPARSED_ARGUMENTS})
+
+ foreach(it ${scxml_files})
+ get_filename_component(outfilename ${it} NAME_WE)
+ get_filename_component(infile ${it} ABSOLUTE)
+ set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${outfilename})
+ set(outfile_cpp ${CMAKE_CURRENT_BINARY_DIR}/${outfilename}.cpp)
+ set(outfile_h ${CMAKE_CURRENT_BINARY_DIR}/${outfilename}.h)
+
+ add_custom_command(OUTPUT ${outfile_cpp} ${outfile_h}
+ COMMAND ${Qt5Scxml_QSCXMLC_EXECUTABLE}
+ ARGS ${ARGS_OPTIONS} --output ${outfile} ${infile}
+ MAIN_DEPENDENCY ${infile}
+ VERBATIM)
+ list(APPEND ${outfiles} ${outfile_cpp})
+ endforeach()
+ set(${outfiles} ${${outfiles}} PARENT_SCOPE)
+endfunction()
diff --git a/src/scxml/doc/external-resources.qdoc b/src/scxml/doc/external-resources.qdoc
index 45c3d08..3cbdc6f 100644
--- a/src/scxml/doc/external-resources.qdoc
+++ b/src/scxml/doc/external-resources.qdoc
@@ -58,3 +58,8 @@
\externalpage http://www.w3.org/TR/scxml/#N11630
\title SCXML Specification - Calculator Example
*/
+
+/*!
+ \externalpage https://tools.ietf.org/html/rfc959
+ \title RFC 959
+*/
diff --git a/src/scxml/doc/qtscxml-examples.qdoc b/src/scxml/doc/qtscxml-examples.qdoc
index a3130bc..cc0b6c4 100644
--- a/src/scxml/doc/qtscxml-examples.qdoc
+++ b/src/scxml/doc/qtscxml-examples.qdoc
@@ -41,8 +41,8 @@ example version folder.
All versions of an example application have the same appearance and
fuctionality. They demonstrate the different options for creating user
-interfaces (using Qt Widgets or Qt Quick) and for loading the SCXML dynamically
-versus first compiling it to a C++ class (the \e static versions).
+interfaces (using \l {Qt Widgets} or \l {Qt Quick}) and for loading the SCXML
+dynamically versus first compiling it to a C++ class (the \e static versions).
In addition, the Media Player example versions demonstrate how to access the C++
and ECMAScript data models.
diff --git a/src/scxml/doc/qtscxml-index.qdoc b/src/scxml/doc/qtscxml-index.qdoc
index 0de9624..7fd77bb 100644
--- a/src/scxml/doc/qtscxml-index.qdoc
+++ b/src/scxml/doc/qtscxml-index.qdoc
@@ -37,9 +37,6 @@
the state machine. It also contains functionality to support data models
and executable content.
- \note In Qt 5.7 the Qt SCXML module is released as Technology Preview.
- This means that the API may still change in subsequent releases.
-
\section1 Getting Started
To include the definitions of the module's classes, use the following directive:
@@ -52,7 +49,7 @@
in your .qml file:
\code
- import QtScxml 5.7
+ import QtScxml 5.8
\endcode
To link against the module, add this line to your qmake .pro file:
diff --git a/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc b/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
index 3b8d6aa..c431653 100644
--- a/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
+++ b/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
@@ -43,7 +43,7 @@
Or, in QML:
\qml
- import QtScxml 5.7
+ import QtScxml 5.8
Item {
property QtObject stateMachine: scxmlLoader.stateMachine
@@ -68,15 +68,22 @@
MyStatemachine stateMachine;
\endcode
- To use a compiled state machine in QML, you can assign it to a context
- property:
+ To use a compiled state machine in QML, you can register it as a QML type:
\code
- MyStatemachine stateMachine;
- QQmlApplicationEngine engine;
- engine.rootContext()->setContextProperty("stateMachine", &stateMachine);
+ qmlRegisterType<MyStateMachine>("MyStateMachine", 1, 0, "MyStateMachine");
\endcode
+ Then you can instantiate it in QML, like this:
+
+ \qml
+ import MyStateMachine 1.0
+
+ MyStateMachine {
+ id: stateMachine
+ }
+ \endqml
+
To compile a state machine, the following lines have to be added to a
.pro file:
@@ -87,7 +94,10 @@
This will tell qmake to run \e qscxmlc which generates MyStatemachine.h
and MyStatemachine.cpp, and adds them to \l [QMake] HEADERS and
- \l [QMake] SOURCES variables.
+ \l [QMake] SOURCES variables. By default, the generated files are saved in
+ the build directory. The \e QSCXMLC_DIR variable can be set to specify
+ another directory. The \e QSCXMLC_NAMESPACE variable can be set to put the
+ state machine code into a C++ namespace.
After instantiating a state machine, you can connect to any state's
active property as follows. For example, if the state machine for a
@@ -95,9 +105,8 @@
convenient id "red" in the scxml file), you can write:
\code
- QObject::connect(stateMachine, &TrafficLightStateMachine::redChanged,
- [](bool active) {
- qDebug() << (active ? "entered" : "exited") << "the red state";
+ stateMachine->connectToState("red", [](bool active) {
+ qDebug() << (active ? "entered" : "exited") << "the red state";
\endcode
And in QML:
@@ -116,7 +125,7 @@
event, you can write:
\code
- QObject::connect(stateMachine, &MediaPlayer::playbackStopped, [](){
+ stateMachine->connectToEvent("playbackStopped", [](const QScxmlEvent &){
qDebug() << "Stopped!";
});
\endcode
@@ -124,17 +133,19 @@
And in QML:
\qml
- Connections {
- target: stateMachine
- onPlaybackStopped: console.log("Stopped!")
+ import QtScxml 5.8
+
+ EventConnection {
+ stateMachine: stateMachine
+ events: ["playbackStopped"]
+ onOccurred: console.log("Stopped!")
}
\endqml
- Sending events to a state machine is equally simple. You can call (or
- connect to) the slot:
+ Sending events to a state machine is equally simple:
\code
- stateMachine->tap(QVariantMap({
+ stateMachine->submitEvent("tap", QVariantMap({
std::make_pair("artist", "Fatboy Slim"),
std::make_pair("title", "The Rockafeller Skank")
});
@@ -144,12 +155,15 @@
_event.data inside the state machine. In QML:
\code
- stateMacine.tap({
+ stateMachine.submitEvent("tap", {
"artist": "Fatboy Slim"
"title": "The Rockafeller Skank"
})
\endcode
- Any invoked state machine with a name property will also show up as a
- property on its parent state machine.
+ \note A state machine needs a \c QEventLoop to work correctly. The event loop is used
+ to implement the \c delay attribute for events and to schedule the processing of a state machine
+ when events are received from nested (or parent) state machines. A QML application or a widget
+ application will always have an event loop running, so nothing extra is needed. For other
+ applications, \c QApplication::run will have to be called to start the event loop processing.
*/
diff --git a/src/scxml/doc/qtscxml-module-qml.qdoc b/src/scxml/doc/qtscxml-module-qml.qdoc
index ddcdf3e..e549f48 100644
--- a/src/scxml/doc/qtscxml-module-qml.qdoc
+++ b/src/scxml/doc/qtscxml-module-qml.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \qmlmodule Scxml 5.7
+ \qmlmodule QtScxml 5.8
\title Qt SCXML QML Types
\ingroup qmlmodules
\brief Enables the use of SCXML state machines with QML.
@@ -35,7 +35,7 @@
in your .qml file:
\code
- import QtScxml 5.7
+ import QtScxml 5.8
\endcode
For more information, see \l{Instantiating State Machines}.
diff --git a/src/scxml/doc/qtscxml-overview.qdoc b/src/scxml/doc/qtscxml-overview.qdoc
index 8b38e39..dfecb0a 100644
--- a/src/scxml/doc/qtscxml-overview.qdoc
+++ b/src/scxml/doc/qtscxml-overview.qdoc
@@ -90,26 +90,6 @@
differ in the way they are instantiated. For more information, see
\l{Instantiating State Machines} and \l{Qt SCXML Examples}.
- \section1 Qt Mode
-
- State machines that are compatible with Qt can be compiled or loaded in the
- \e {Qt mode} to fully benefit from Qt. The Qt mode is unconditionally
- enabled by adding the following line to the beginning of the SCXML file:
- \c {<!-- enable-qt-mode: yes -->}. It is unconditionally disabled by the
- value \c no.
-
- The Qt mode should be disabled for random SCXML files that do not comply to
- the Qt rules. For example, for an SCXML file to be compatible with the Qt
- mode, all IDs and names must be valid Qt identifiers.
-
- In the Qt mode, external signals defined inside an SCXML file that are of
- the \c qt:signal type, are accessible as signals of QScxmlStateMachine. As
- an argument, they take QVariant. It is of QMap<QString, QVariant> type and
- contains the content of all the \c <param> elements specified as children of
- a \c <send> element. The name of each QScxmlStateMachine signal corresponds
- to the value defined in the \e event attribute of one \c <send> tag in the
- SCXML file.
-
\section1 Logging Categories
The Qt SCXML module exports the following logging categories:
diff --git a/src/scxml/doc/qtscxml-scxml-compliance.qdoc b/src/scxml/doc/qtscxml-scxml-compliance.qdoc
index e86feca..ab1d730 100644
--- a/src/scxml/doc/qtscxml-scxml-compliance.qdoc
+++ b/src/scxml/doc/qtscxml-scxml-compliance.qdoc
@@ -72,19 +72,6 @@
The Qt SCXML implementation extends SCXML in the following ways:
\list
- \li For communication purposes, the \c <send> tag supports an extra
- value \c "qt:signal" accepted by the attribute \c type.
- The generated state machine or the state machine that has loaded
- the SCXML file dynamically will contain the corresponding
- signal, the name of which will be equal to the value of the
- \e event attribute inside the \c <send> tag.
- All of the \c <param> children will be placed inside the
- QMap<QString, QVariant> map, and this map will be passed as a
- QVariant argument of the specified signal.
- \li State machines that are compatible with Qt can be compiled or loaded in
- the \e {Qt mode} to fully benefit from Qt. The Qt mode is enabled by
- adding the following line to the beginning of the SCXML file:
- \c {<!-- enable-qt-mode: yes -->}.
\li If the event is an error event, \c _event.errorMessage will contain a
more detailed description of the error.
\endlist
diff --git a/src/scxml/doc/qtscxml.qdocconf b/src/scxml/doc/qtscxml.qdocconf
index d7e5f1a..0e794d9 100644
--- a/src/scxml/doc/qtscxml.qdocconf
+++ b/src/scxml/doc/qtscxml.qdocconf
@@ -34,7 +34,7 @@ qhp.QtScxml.virtualFolder = qtscxml
qhp.QtScxml.indexTitle = Qt SCXML
qhp.QtScxml.indexRoot =
-depends += qtcore qtdeclarative qtdoc qmake qtwidgets
+depends += qtcore qtdoc qmake qtquick qtwidgets
headerdirs = .. ../../imports/scxmlstatemachine
sourcedirs += .. \
diff --git a/src/scxml/qscxmlparser.cpp b/src/scxml/qscxmlcompiler.cpp
index 3906ada..691f87a 100644
--- a/src/scxml/qscxmlparser.cpp
+++ b/src/scxml/qscxmlcompiler.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qscxmlparser_p.h"
+#include "qscxmlcompiler_p.h"
#include "qscxmlexecutablecontent_p.h"
#include <QXmlStreamReader>
@@ -49,12 +49,12 @@
#include <QString>
#ifndef BUILD_QSCXMLC
-#include "qscxmlnulldatamodel.h"
#include "qscxmlecmascriptdatamodel.h"
-#include "qscxmlqstates.h"
+#include "qscxmlinvokableservice_p.h"
#include "qscxmldatamodel_p.h"
#include "qscxmlstatemachine_p.h"
#include "qscxmlstatemachine.h"
+#include "qscxmltabledata_p.h"
#include <QState>
#include <QHistoryState>
@@ -96,12 +96,12 @@ public:
doc->isVerified = true;
m_doc = doc;
- foreach (DocumentModel::AbstractState *state, doc->allStates) {
+ for (DocumentModel::AbstractState *state : qAsConst(doc->allStates)) {
if (state->id.isEmpty()) {
continue;
#ifndef QT_NO_DEBUG
} else if (m_stateById.contains(state->id)) {
- Q_ASSERT(!"Should be unreachable: the parser should check for this case!");
+ Q_ASSERT(!"Should be unreachable: the compiler should check for this case!");
#endif // QT_NO_DEBUG
} else {
m_stateById[state->id] = state;
@@ -116,7 +116,6 @@ public:
private:
bool visit(DocumentModel::Scxml *scxml) Q_DECL_OVERRIDE
{
- Q_ASSERT(scxml->initialStates.isEmpty());
if (!scxml->name.isEmpty() && !isValidToken(scxml->name, XmlNmtoken)) {
error(scxml->xmlLocation,
QStringLiteral("scxml name '%1' is not a valid XML Nmtoken").arg(scxml->name));
@@ -124,15 +123,17 @@ private:
if (scxml->initial.isEmpty()) {
if (auto firstChild = firstAbstractState(scxml)) {
- scxml->initialStates.append(firstChild);
+ scxml->initialTransition = createInitialTransition({firstChild});
}
} else {
- foreach (const QString &initial, scxml->initial) {
+ QVector<DocumentModel::AbstractState *> initialStates;
+ for (const QString &initial : qAsConst(scxml->initial)) {
if (DocumentModel::AbstractState *s = m_stateById.value(initial))
- scxml->initialStates.append(s);
+ initialStates.append(s);
else
error(scxml->xmlLocation, QStringLiteral("initial state '%1' not found for <scxml> element").arg(initial));
}
+ scxml->initialTransition = createInitialTransition(initialStates);
}
m_parentNodes.append(scxml);
@@ -147,28 +148,41 @@ private:
bool visit(DocumentModel::State *state) Q_DECL_OVERRIDE
{
- Q_ASSERT(state->initialStates.isEmpty());
-
if (!state->id.isEmpty() && !isValidToken(state->id, XmlNCName)) {
error(state->xmlLocation, QStringLiteral("'%1' is not a valid XML ID").arg(state->id));
- } else if (state->type != DocumentModel::State::Initial) {
- validateStateName(state->xmlLocation, state->id);
}
- if (state->initial.isEmpty()) {
- if (auto firstChild = firstAbstractState(state)) {
- state->initialStates += firstChild;
- }
- } else {
- Q_ASSERT(state->type == DocumentModel::State::Normal);
- foreach (const QString &initialState, state->initial) {
- if (DocumentModel::AbstractState *s = m_stateById.value(initialState)) {
- state->initialStates += s;
+ if (state->initialTransition == nullptr) {
+ if (state->initial.isEmpty()) {
+ if (state->type == DocumentModel::State::Parallel) {
+ auto allChildren = allAbstractStates(state);
+ state->initialTransition = createInitialTransition(allChildren);
} else {
- error(state->xmlLocation,
- QStringLiteral("undefined initial state '%1' for state '%2'")
- .arg(initialState, state->id));
+ if (auto firstChild = firstAbstractState(state)) {
+ state->initialTransition = createInitialTransition({firstChild});
+ }
+ }
+ } else {
+ Q_ASSERT(state->type == DocumentModel::State::Normal);
+ QVector<DocumentModel::AbstractState *> initialStates;
+ for (const QString &initialState : qAsConst(state->initial)) {
+ if (DocumentModel::AbstractState *s = m_stateById.value(initialState)) {
+ initialStates.append(s);
+ } else {
+ error(state->xmlLocation,
+ QStringLiteral("undefined initial state '%1' for state '%2'")
+ .arg(initialState, state->id));
+ }
}
+ state->initialTransition = createInitialTransition(initialStates);
+ }
+ } else {
+ if (state->initial.isEmpty()) {
+ visit(state->initialTransition);
+ } else {
+ error(state->xmlLocation,
+ QStringLiteral("initial transition and initial attribute for state '%1'")
+ .arg(state->id));
}
}
@@ -177,27 +191,8 @@ private:
break;
case DocumentModel::State::Parallel:
if (!state->initial.isEmpty()) {
- error(state->xmlLocation, QStringLiteral("parallel states cannot have an initial state"));
- }
- break;
- case DocumentModel::State::Initial:
- if (transitionCount(state) != 1)
- error(state->xmlLocation, QStringLiteral("an initial state can only have one transition, but has '%1'").arg(transitionCount(state)));
- if (DocumentModel::Transition *t = firstTransition(state)) {
- if (!t->events.isEmpty() || !t->condition.isNull()) {
- error(t->xmlLocation, QStringLiteral("the transition in an initial state cannot have an event or a condition"));
- }
- if (t->targets.isEmpty()) {
- error(t->xmlLocation, QStringLiteral("the transition in an initial state must have at least one target"));
- }
- }
- foreach (DocumentModel::StateOrTransition *child, state->children) {
- if (DocumentModel::State *s = child->asState()) {
- error(s->xmlLocation, QStringLiteral("substates are not allowed in initial states"));
- }
- }
- if (parentState() == Q_NULLPTR) {
- error(state->xmlLocation, QStringLiteral("initial states can only occur in a state"));
+ error(state->xmlLocation,
+ QStringLiteral("parallel states cannot have an initial state"));
}
break;
case DocumentModel::State::Final:
@@ -221,7 +216,7 @@ private:
if (int size = transition->targets.size())
transition->targetStates.reserve(size);
- foreach (const QString &target, transition->targets) {
+ for (const QString &target : qAsConst(transition->targets)) {
if (DocumentModel::AbstractState *s = m_stateById.value(target)) {
if (transition->targetStates.contains(s)) {
error(transition->xmlLocation, QStringLiteral("duplicate target '%1'").arg(target));
@@ -232,15 +227,9 @@ private:
error(transition->xmlLocation, QStringLiteral("unknown state '%1' in target").arg(target));
}
}
- foreach (const QString &event, transition->events) {
+ for (const QString &event : qAsConst(transition->events))
checkEvent(event, transition->xmlLocation, AllowWildCards);
- if (!DocumentModel::isEventToBeGenerated(event))
- continue;
-
- validateEventName(transition->xmlLocation, event);
- }
-
m_parentNodes.append(transition);
return true;
}
@@ -253,7 +242,7 @@ private:
bool visit(DocumentModel::HistoryState *state) Q_DECL_OVERRIDE
{
bool seenTransition = false;
- foreach (DocumentModel::StateOrTransition *sot, state->children) {
+ for (DocumentModel::StateOrTransition *sot : qAsConst(state->children)) {
if (DocumentModel::State *s = sot->asState()) {
error(s->xmlLocation, QStringLiteral("history state cannot have substates"));
} else if (DocumentModel::Transition *t = sot->asTransition()) {
@@ -274,11 +263,6 @@ private:
bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE
{
checkEvent(node->event, node->xmlLocation, ForbidWildCards);
-
- if (node->type == QStringLiteral("qt:signal")) {
- validateEventName(node->xmlLocation, node->event);
- }
-
checkExpr(node->xmlLocation, QStringLiteral("send"), QStringLiteral("eventexpr"), node->eventexpr);
return true;
}
@@ -296,6 +280,9 @@ private:
bool visit(DocumentModel::Invoke *node) Q_DECL_OVERRIDE
{
+ if (!node->srcexpr.isEmpty())
+ return false;
+
if (node->content.isNull()) {
error(node->xmlLocation, QStringLiteral("no valid content found in <invoke> tag"));
} else {
@@ -409,321 +396,72 @@ private:
return true;
}
- static int transitionCount(DocumentModel::State *state)
- {
- int count = 0;
- foreach (DocumentModel::StateOrTransition *child, state->children) {
- if (child->asTransition())
- ++count;
- }
- return count;
- }
-
- static DocumentModel::Transition *firstTransition(DocumentModel::State *state)
- {
- foreach (DocumentModel::StateOrTransition *child, state->children) {
- if (DocumentModel::Transition *t = child->asTransition())
- return t;
- }
- return Q_NULLPTR;
- }
-
- static DocumentModel::AbstractState *firstAbstractState(DocumentModel::StateContainer *container)
+ static const QVector<DocumentModel::StateOrTransition *> &allChildrenOfContainer(
+ DocumentModel::StateContainer *container)
{
- QVector<DocumentModel::StateOrTransition *> children;
if (auto state = container->asState())
- children = state->children;
+ return state->children;
else if (auto scxml = container->asScxml())
- children = scxml->children;
+ return scxml->children;
else
Q_UNREACHABLE();
- foreach (DocumentModel::StateOrTransition *child, children) {
- if (DocumentModel::State *s = child->asState())
- return s;
- else if (DocumentModel::HistoryState *h = child->asHistoryState())
- return h;
- }
- return Q_NULLPTR;
- }
-
- void checkExpr(const DocumentModel::XmlLocation &loc, const QString &tag, const QString &attrName, const QString &attrValue)
- {
- if (m_doc->root->dataModel == DocumentModel::Scxml::NullDataModel && !attrValue.isEmpty()) {
- error(loc, QStringLiteral(
- "%1 in <%2> cannot be used with data model 'null'").arg(attrName, tag));
- }
}
- void error(const DocumentModel::XmlLocation &location, const QString &message)
+ static DocumentModel::AbstractState *firstAbstractState(DocumentModel::StateContainer *container)
{
- m_hasErrors = true;
- if (m_errorHandler)
- m_errorHandler(location, message);
- }
+ const auto &allChildren = allChildrenOfContainer(container);
- DocumentModel::Node *parentState() const
- {
- for (int i = m_parentNodes.size() - 1; i >= 0; --i) {
- if (DocumentModel::State *s = m_parentNodes.at(i)->asState())
+ QVector<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *child : qAsConst(allChildren)) {
+ if (DocumentModel::State *s = child->asState())
return s;
+ else if (DocumentModel::HistoryState *h = child->asHistoryState())
+ return h;
}
-
- return Q_NULLPTR;
+ return nullptr;
}
- void validateEventName(const DocumentModel::XmlLocation &location, const QString &name)
+ static QVector<DocumentModel::AbstractState *> allAbstractStates(
+ DocumentModel::StateContainer *container)
{
- if (!m_doc->qtMode)
- return;
-
- if (!isValidCppIdentifier(name))
- error(location, QStringLiteral(
- "event name '%1' is not a valid C++ identifier in Qt mode").arg(name));
-
- if (!isValidQtIdentifier(name))
- error(location, QStringLiteral(
- "event name '%1' is not a valid Qt identifier in Qt mode").arg(name));
-
- if (m_stateById.contains(name))
- error(location, QStringLiteral(
- "event name '%1' collides with a state name '%1' in Qt mode").arg(name));
+ const auto &allChildren = allChildrenOfContainer(container);
- const QString changedSuffix(QStringLiteral("Changed"));
- if (name.endsWith(changedSuffix)) {
- const QString baseName = name.left(name.count() - changedSuffix.count());
- if (m_stateById.contains(baseName))
- error(location, QStringLiteral(
- "event name '%1' collides with a state name '%2' in Qt mode").arg(name).arg(baseName));
+ QVector<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *child : qAsConst(allChildren)) {
+ if (DocumentModel::State *s = child->asState())
+ childStates.append(s);
+ else if (DocumentModel::HistoryState *h = child->asHistoryState())
+ childStates.append(h);
}
+ return childStates;
}
- void validateStateName(const DocumentModel::XmlLocation &location, const QString &name)
+ DocumentModel::Transition *createInitialTransition(
+ const QVector<DocumentModel::AbstractState *> &states)
{
- if (!m_doc->qtMode)
- return;
-
- if (!isValidCppIdentifier(name))
- error(location, QStringLiteral(
- "state name '%1' is not a valid C++ identifier in Qt mode").arg(name));
-
- if (!isValidQtIdentifier(name))
- error(location, QStringLiteral(
- "state name '%1' is not a valid Qt identifier in Qt mode").arg(name));
-
- const QString changedSuffix(QStringLiteral("Changed"));
- if (name.endsWith(changedSuffix)) {
- const QString baseName = name.left(name.count() - changedSuffix.count());
- if (m_stateById.contains(baseName))
- error(location, QStringLiteral(
- "state name '%1' collides with a state name '%2' in Qt mode").arg(name).arg(baseName));
+ auto *newTransition = m_doc->newTransition(nullptr, DocumentModel::XmlLocation(-1, -1));
+ newTransition->type = DocumentModel::Transition::Synthetic;
+ for (auto *s : states) {
+ newTransition->targets.append(s->id);
}
+
+ newTransition->targetStates = states;
+ return newTransition;
}
- static bool isValidCppIdentifier(const QString &str)
+ void checkExpr(const DocumentModel::XmlLocation &loc, const QString &tag, const QString &attrName, const QString &attrValue)
{
- static const QStringList keywords = QStringList()
- << QStringLiteral("alignas")
- << QStringLiteral("alignof")
- << QStringLiteral("asm")
- << QStringLiteral("auto")
- << QStringLiteral("bool")
- << QStringLiteral("break")
- << QStringLiteral("case")
- << QStringLiteral("catch")
- << QStringLiteral("char")
- << QStringLiteral("char16_t")
- << QStringLiteral("char32_t")
- << QStringLiteral("class")
- << QStringLiteral("const")
- << QStringLiteral("constexpr")
- << QStringLiteral("const_cast")
- << QStringLiteral("continue")
- << QStringLiteral("decltype")
- << QStringLiteral("default")
- << QStringLiteral("delete")
- << QStringLiteral("double")
- << QStringLiteral("do")
- << QStringLiteral("dynamic_cast")
- << QStringLiteral("else")
- << QStringLiteral("enum")
- << QStringLiteral("explicit")
- << QStringLiteral("export")
- << QStringLiteral("extern")
- << QStringLiteral("false")
- << QStringLiteral("float")
- << QStringLiteral("for")
- << QStringLiteral("friend")
- << QStringLiteral("goto")
- << QStringLiteral("if")
- << QStringLiteral("inline")
- << QStringLiteral("int")
- << QStringLiteral("long")
- << QStringLiteral("mutable")
- << QStringLiteral("namespace")
- << QStringLiteral("new")
- << QStringLiteral("noexcept")
- << QStringLiteral("nullptr")
- << QStringLiteral("operator")
- << QStringLiteral("private")
- << QStringLiteral("protected")
- << QStringLiteral("public")
- << QStringLiteral("register")
- << QStringLiteral("reinterpret_cast")
- << QStringLiteral("return")
- << QStringLiteral("short")
- << QStringLiteral("signed")
- << QStringLiteral("sizeof")
- << QStringLiteral("static")
- << QStringLiteral("static_assert")
- << QStringLiteral("static_cast")
- << QStringLiteral("struct")
- << QStringLiteral("switch")
- << QStringLiteral("template")
- << QStringLiteral("this")
- << QStringLiteral("thread_local")
- << QStringLiteral("throw")
- << QStringLiteral("true")
- << QStringLiteral("try")
- << QStringLiteral("typedef")
- << QStringLiteral("typeid")
- << QStringLiteral("typename")
- << QStringLiteral("union")
- << QStringLiteral("unsigned")
- << QStringLiteral("using")
- << QStringLiteral("virtual")
- << QStringLiteral("void")
- << QStringLiteral("volatile")
- << QStringLiteral("wchar_t")
- << QStringLiteral("while");
-
- if (keywords.contains(str)) {
- return false;
- }
-
- auto isNonDigit = [](QChar ch) -> bool {
- return (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))
- || (ch >= QLatin1Char('a') && ch <= QLatin1Char('z'))
- || (ch == QLatin1Char('_'));
- };
-
- QChar ch = str.at(0);
- if (!isNonDigit(ch)) {
- return false;
- }
- for (int i = 1, ei = str.size(); i != ei; ++i) {
- ch = str.at(i);
- if (!isNonDigit(ch) && !ch.isDigit()) {
- return false;
- }
- }
-
- if (str.startsWith(QLatin1Char('_')) && str.size() > 1) {
- QChar ch = str.at(1);
- if (ch == QLatin1Char('_')
- || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) {
- return false;
- }
+ if (m_doc->root->dataModel == DocumentModel::Scxml::NullDataModel && !attrValue.isEmpty()) {
+ error(loc, QStringLiteral(
+ "%1 in <%2> cannot be used with data model 'null'").arg(attrName, tag));
}
-
- return true;
}
- static bool isValidQtIdentifier(const QString &str)
+ void error(const DocumentModel::XmlLocation &location, const QString &message)
{
- static const QStringList keywords = QStringList()
- << QStringLiteral("QObject")
- << QStringLiteral("event")
- << QStringLiteral("eventFilter")
- << QStringLiteral("tr")
- << QStringLiteral("trUtf8")
- << QStringLiteral("metaObject")
- << QStringLiteral("staticMetaObject")
- << QStringLiteral("objectName")
- << QStringLiteral("setObjectName")
- << QStringLiteral("isWidgetType")
- << QStringLiteral("isWindowType")
- << QStringLiteral("signalsBlocked")
- << QStringLiteral("blockSignals")
- << QStringLiteral("thread")
- << QStringLiteral("moveToThread")
- << QStringLiteral("startTimer")
- << QStringLiteral("killTimer")
- << QStringLiteral("findChild")
- << QStringLiteral("findChildren")
- << QStringLiteral("children")
- << QStringLiteral("setParent")
- << QStringLiteral("installEventFilter")
- << QStringLiteral("removeEventFilter")
- << QStringLiteral("connect")
- << QStringLiteral("connect_functor")
- << QStringLiteral("disconnect")
- << QStringLiteral("dumpObjectTree")
- << QStringLiteral("dumpObjectInfo")
- << QStringLiteral("setProperty")
- << QStringLiteral("property")
- << QStringLiteral("dynamicPropertyNames")
- << QStringLiteral("registerUserData")
- << QStringLiteral("setUserData")
- << QStringLiteral("userData")
- << QStringLiteral("destroyed")
- << QStringLiteral("objectNameChanged")
- << QStringLiteral("parent")
- << QStringLiteral("inherits")
- << QStringLiteral("deleteLater")
- << QStringLiteral("sender")
- << QStringLiteral("senderSignalIndex")
- << QStringLiteral("receivers")
- << QStringLiteral("isSignalConnected")
- << QStringLiteral("timerEvent")
- << QStringLiteral("childEvent")
- << QStringLiteral("customEvent")
- << QStringLiteral("connectNotify")
- << QStringLiteral("disconnectNotify")
- << QStringLiteral("d_ptr")
- << QStringLiteral("staticQtMetaObject")
- << QStringLiteral("d_func")
- << QStringLiteral("connectImpl")
- << QStringLiteral("disconnectImpl")
-
- << QStringLiteral("QScxmlStateMachine")
- << QStringLiteral("running")
- << QStringLiteral("BindingMethod")
- << QStringLiteral("EarlyBinding")
- << QStringLiteral("LateBinding")
- << QStringLiteral("fromFile")
- << QStringLiteral("fromData")
- << QStringLiteral("parseErrors")
- << QStringLiteral("sessionId")
- << QStringLiteral("setSessionId")
- << QStringLiteral("generateSessionId")
- << QStringLiteral("isInvoked")
- << QStringLiteral("dataModel")
- << QStringLiteral("dataBinding")
- << QStringLiteral("init")
- << QStringLiteral("isRunning")
- << QStringLiteral("name")
- << QStringLiteral("stateNames")
- << QStringLiteral("activeStateNames")
- << QStringLiteral("isActive")
- << QStringLiteral("scxmlEventFilter")
- << QStringLiteral("setScxmlEventFilter")
- << QStringLiteral("submitEvent")
- << QStringLiteral("cancelDelayedEvent")
- << QStringLiteral("isDispatchableTarget")
- << QStringLiteral("runningChanged")
- << QStringLiteral("log")
- << QStringLiteral("reachedStableState")
- << QStringLiteral("finished")
- << QStringLiteral("eventOccurred")
- << QStringLiteral("start")
- << QStringLiteral("setDataBinding")
- << QStringLiteral("setService")
- << QStringLiteral("tableData");
-
- if (keywords.contains(str))
- return false;
-
- return true;
+ m_hasErrors = true;
+ if (m_errorHandler)
+ m_errorHandler(location, message);
}
private:
@@ -735,22 +473,48 @@ private:
};
#ifndef BUILD_QSCXMLC
-class QStateMachineBuilder;
-class DynamicStateMachine: public QScxmlStateMachine, public QScxmlEventFilter
+class InvokeDynamicScxmlFactory: public QScxmlScxmlServiceFactory
+{
+public:
+ InvokeDynamicScxmlFactory(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &params)
+ : QScxmlScxmlServiceFactory(invokeInfo, namelist, params)
+ {}
+
+ void setContent(const QSharedPointer<DocumentModel::ScxmlDocument> &content)
+ { m_content = content; }
+
+ QScxmlInvokableService *invoke(QScxmlStateMachine *child) Q_DECL_OVERRIDE;
+
+private:
+ QSharedPointer<DocumentModel::ScxmlDocument> m_content;
+};
+
+class DynamicStateMachinePrivate : public QScxmlStateMachinePrivate
+{
+public:
+ DynamicStateMachinePrivate() :
+ QScxmlStateMachinePrivate(&QScxmlStateMachine::staticMetaObject) {}
+};
+
+class DynamicStateMachine: public QScxmlStateMachine, public QScxmlInternal::GeneratedTableData
{
+ Q_DECLARE_PRIVATE(DynamicStateMachine)
// Manually expanded from Q_OBJECT macro:
public:
Q_OBJECT_CHECK
const QMetaObject *metaObject() const Q_DECL_OVERRIDE
- { return m_metaObject; }
+ { return d_func()->m_metaObject; }
int qt_metacall(QMetaObject::Call _c, int _id, void **_a) Q_DECL_OVERRIDE
{
+ Q_D(DynamicStateMachine);
_id = QScxmlStateMachine::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
- int ownMethodCount = m_metaObject->methodCount() - m_metaObject->methodOffset();
+ int ownMethodCount = d->m_metaObject->methodCount() - d->m_metaObject->methodOffset();
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < ownMethodCount)
qt_static_metacall(this, _c, _id, _a);
@@ -758,7 +522,7 @@ public:
} else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
|| _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {
qt_static_metacall(this, _c, _id, _a);
- _id -= m_metaObject->propertyCount();
+ _id -= d->m_metaObject->propertyCount();
}
return _id;
}
@@ -766,89 +530,40 @@ public:
private:
static void qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
- if (_c == QMetaObject::InvokeMetaMethod) {
- DynamicStateMachine *_t = static_cast<DynamicStateMachine *>(_o);
- if (_id >= _t->m_eventNamesByIndex.size() || _id < 0) {
- // out of bounds
- return;
- }
- if (_id >= _t->m_firstSubStateMachineSignal && _id < _t->m_firstSlot) {
- // these signals are only emitted, not activated by another signal
- return;
- }
- if (_id >= _t->m_firstStateChangedSignal && _id < _t->m_firstSubStateMachineSignal) {
- // re-propagate QAbstractState::activeChanged as stateChanged
- QMetaObject::activate(_t, _t->m_metaObject, _id, _a);
- return;
- }
- // We have 1 kind of slots: those to submit events.
- const QString &event = _t->m_eventNamesByIndex.at(_id);
- if (!event.isEmpty()) {
- if (_id < _t->m_firstSlotWithoutData) {
- QVariant data = *reinterpret_cast< QVariant(*)>(_a[1]);
- if (data.canConvert<QJSValue>()) {
- data = data.value<QJSValue>().toVariant();
- }
- _t->submitEvent(event, data);
- } else {
- _t->submitEvent(event, QVariant());
- }
- }
- } else if (_c == QMetaObject::RegisterPropertyMetaType) {
- DynamicStateMachine *_t = static_cast<DynamicStateMachine *>(_o);
- if (_id < _t->m_firstSubStateMachineProperty) {
- *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType<bool>();
- } else {
- *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType<QScxmlStateMachine *>();
- }
+ if (_c == QMetaObject::RegisterPropertyMetaType) {
+ *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType<bool>();
} else if (_c == QMetaObject::ReadProperty) {
DynamicStateMachine *_t = static_cast<DynamicStateMachine *>(_o);
void *_v = _a[0];
- if (_id >= 0 && _id < _t->m_propertyNamesByIndex.size()) {
- if (_id < _t->m_firstSubStateMachineProperty) {
- // getter for the state
- auto smp = QScxmlStateMachinePrivate::get(_t);
- auto name = _t->m_propertyNamesByIndex.at(_id);
- *reinterpret_cast<bool*>(_v) = smp->stateByScxmlName(name)->active();
- } else {
- // getter for a child statemachine
- int idx = _id - _t->m_firstSubStateMachineProperty;
- *reinterpret_cast<QScxmlStateMachine **>(_v) = _t->m_subStateMachines.at(idx);
- }
+ if (_id >= 0 && _id < _t->m_propertyCount) {
+ // getter for the state
+ *reinterpret_cast<bool*>(_v) = _t->isActive(_id);
}
}
}
// end of Q_OBJECT macro
private:
- friend QStateMachineBuilder;
DynamicStateMachine()
- : m_metaObject(Q_NULLPTR)
- , m_firstSubStateMachineSignal(0)
- , m_firstSlot(0)
- , m_firstSlotWithoutData(0)
- , m_firstSubStateMachineProperty(0)
+ : QScxmlStateMachine(*new DynamicStateMachinePrivate)
+ , m_propertyCount(0)
{
- // Temporarily wire up the QMetaObject, because qobject_cast needs it while building MyQStateMachine.
+ // Temporarily wire up the QMetaObject
+ Q_D(DynamicStateMachine);
QMetaObjectBuilder b;
b.setClassName("DynamicStateMachine");
b.setSuperClass(&QScxmlStateMachine::staticMetaObject);
b.setStaticMetacallFunction(qt_static_metacall);
- m_metaObject = b.toMetaObject();
-
- setScxmlEventFilter(this);
+ d->m_metaObject = b.toMetaObject();
}
- void initDynamicParts(const QSet<QString> &eventSignals,
- const QSet<QString> &eventSlots,
- const QList<QString> &stateNames,
- const QList<QString> &subStateMachineNames)
+ void initDynamicParts(const MetaDataInfo &info)
{
+ Q_D(DynamicStateMachine);
// Release the temporary QMetaObject.
- Q_ASSERT(m_metaObject);
- free(m_metaObject);
-
- m_eventNamesByIndex.reserve(eventSignals.size() + subStateMachineNames.size() + eventSlots.size());
+ Q_ASSERT(d->m_metaObject != &QScxmlStateMachine::staticMetaObject);
+ free(const_cast<QMetaObject *>(d->m_metaObject));
+ d->m_metaObject = &QScxmlStateMachine::staticMetaObject;
// Build the real one.
QMetaObjectBuilder b;
@@ -857,126 +572,60 @@ private:
b.setStaticMetacallFunction(qt_static_metacall);
// signals
- foreach (const QString &eventName, eventSignals) {
- QByteArray signalName = eventName.toUtf8() + "(const QVariant &)";
- QMetaMethodBuilder signalBuilder = b.addSignal(signalName);
- signalBuilder.setParameterNames(init("data"));
- int idx = signalBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- m_eventNamesByIndex[idx] = eventName;
- }
-
- m_firstStateChangedSignal = m_eventNamesByIndex.size();
- foreach (const QString &stateName, stateNames) {
+ for (const QString &stateName : info.stateNames) {
auto name = stateName.toUtf8();
- QByteArray signalName = name + "Changed(bool)";
+ const QByteArray signalName = name + "Changed(bool)";
QMetaMethodBuilder signalBuilder = b.addSignal(signalName);
signalBuilder.setParameterNames(init("active"));
- int idx = signalBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- }
-
- m_firstSubStateMachineSignal = m_eventNamesByIndex.size();
- foreach (const QString &machineName, subStateMachineNames) {
- auto name = machineName.toUtf8();
- QByteArray signalName = name + "Changed(QScxmlStateMachine *)";
- QMetaMethodBuilder signalBuilder = b.addSignal(signalName);
- signalBuilder.setParameterNames(init("statemachine"));
- int idx = signalBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- }
-
- // slots
- m_firstSlot = m_eventNamesByIndex.size();
- foreach (const QString &eventName, eventSlots) {
- QByteArray slotName = eventName.toUtf8() + "(const QVariant &)";
- QMetaMethodBuilder slotBuilder = b.addSlot(slotName);
- slotBuilder.setParameterNames(init("data"));
- int idx = slotBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- m_eventNamesByIndex[idx] = eventName;
- }
-
- m_firstSlotWithoutData = m_eventNamesByIndex.size();
- foreach (const QString &eventName, eventSlots) {
- QByteArray slotName = eventName.toUtf8() + "()";
- QMetaMethodBuilder slotBuilder = b.addSlot(slotName);
- int idx = slotBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- m_eventNamesByIndex[idx] = eventName;
}
// properties
- int stateNotifier = m_firstStateChangedSignal;
- foreach (const QString &stateName, stateNames) {
- QMetaPropertyBuilder prop = b.addProperty(stateName.toUtf8(), "bool", stateNotifier);
+ int notifier = 0;
+ for (const QString &stateName : info.stateNames) {
+ QMetaPropertyBuilder prop = b.addProperty(stateName.toUtf8(), "bool", notifier);
prop.setWritable(false);
- int idx = prop.index();
- m_propertyNamesByIndex.resize(std::max(idx + 1, m_propertyNamesByIndex.size()));
- m_propertyNamesByIndex[idx] = stateName;
- ++stateNotifier;
- }
-
- m_firstSubStateMachineProperty = m_propertyNamesByIndex.size();
- int notifier = m_firstSubStateMachineSignal;
- foreach (const QString &machineName, subStateMachineNames) {
- QMetaPropertyBuilder prop = b.addProperty(machineName.toUtf8(), "QScxmlStateMachine *", notifier);
- prop.setWritable(false);
- int idx = prop.index();
- m_propertyNamesByIndex.resize(std::max(idx + 1, m_propertyNamesByIndex.size()));
- m_propertyNamesByIndex[idx] = machineName;
+ ++m_propertyCount;
++notifier;
}
- m_subStateMachines.resize(subStateMachineNames.size());
// And we're done
- m_metaObject = b.toMetaObject();
+ d->m_metaObject = b.toMetaObject();
}
public:
~DynamicStateMachine()
- { if (m_metaObject) free(m_metaObject); }
-
- bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) Q_DECL_OVERRIDE {
- Q_UNUSED(stateMachine);
-
- if (event->originType() != QStringLiteral("qt:signal")) {
- return true;
- }
-
- auto eventName = event->name();
- for (int i = 0; i < m_firstSubStateMachineSignal; ++i) {
- if (m_eventNamesByIndex.at(i) == eventName) {
- QVariant data = event->data();
- void *argv[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&data)) };
- QMetaObject::activate(this, metaObject(), i, argv);
- return false;
- }
+ {
+ Q_D(DynamicStateMachine);
+ if (d->m_metaObject != &QScxmlStateMachine::staticMetaObject) {
+ free(const_cast<QMetaObject *>(d->m_metaObject));
+ d->m_metaObject = &QScxmlStateMachine::staticMetaObject;
}
-
- return true;
}
-protected:
- void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE
+ QScxmlInvokableServiceFactory *serviceFactory(int id) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return m_allFactoriesById.at(id); }
+
+ static DynamicStateMachine *build(DocumentModel::ScxmlDocument *doc)
{
- int idx = -1;
- for (int i = m_firstSubStateMachineProperty, ei = m_propertyNamesByIndex.size(); i != ei; ++i) {
- if (m_propertyNamesByIndex.at(i) == id) {
- idx = i - m_firstSubStateMachineProperty;
- break;
- }
- }
- if (idx < 0)
- return;
- auto scxml = service ? dynamic_cast<QScxmlInvokableScxml *>(service) : Q_NULLPTR;
- auto machine = scxml ? scxml->stateMachine() : Q_NULLPTR;
- if (m_subStateMachines.at(idx) != machine) {
- m_subStateMachines[idx] = machine;
- // emit changed signal:
- void *argv[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&machine)) };
- QMetaObject::activate(this, metaObject(), m_firstSubStateMachineSignal + idx, argv);
- }
+ auto stateMachine = new DynamicStateMachine;
+ MetaDataInfo info;
+ DataModelInfo dm;
+ auto factoryIdCreator = [stateMachine](
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &params,
+ const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int {
+ auto factory = new InvokeDynamicScxmlFactory(invokeInfo, namelist, params);
+ factory->setContent(content);
+ stateMachine->m_allFactoriesById.append(factory);
+ return stateMachine->m_allFactoriesById.size() - 1;
+ };
+
+ GeneratedTableData::build(doc, stateMachine, &info, &dm, factoryIdCreator);
+ stateMachine->setTableData(stateMachine);
+ stateMachine->initDynamicParts(info);
+
+ return stateMachine;
}
private:
@@ -990,438 +639,106 @@ private:
}
private:
- QMetaObject *m_metaObject;
- QVector<QString> m_eventNamesByIndex;
- QVector<QString> m_propertyNamesByIndex;
- QVector<QScxmlStateMachine *> m_subStateMachines;
- int m_firstSubStateMachineSignal;
- int m_firstStateChangedSignal;
- int m_firstSlot;
- int m_firstSlotWithoutData;
- int m_firstSubStateMachineProperty;
+ QVector<QScxmlInvokableServiceFactory *> m_allFactoriesById;
+ int m_propertyCount;
};
-class InvokeDynamicScxmlFactory: public QScxmlInvokableScxmlServiceFactory
+inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke(
+ QScxmlStateMachine *parentStateMachine)
{
-public:
- InvokeDynamicScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool autoforward,
- const QVector<Param> &params,
- QScxmlExecutableContent::ContainerId finalize)
- : QScxmlInvokableScxmlServiceFactory(invokeLocation, id, idPrefix, idlocation, namelist, autoforward, params, finalize)
- {}
-
- void setContent(const QSharedPointer<DocumentModel::ScxmlDocument> &content)
- { m_content = content; }
-
- QScxmlInvokableService *invoke(QScxmlStateMachine *child) Q_DECL_OVERRIDE;
-
-private:
- QSharedPointer<DocumentModel::ScxmlDocument> m_content;
-};
-
-class QStateMachineBuilder: public QScxmlExecutableContent::Builder
-{
-public:
- QStateMachineBuilder()
- : m_stateMachine(Q_NULLPTR)
- , m_currentTransition(Q_NULLPTR)
- , m_bindLate(false)
- , m_qtMode(false)
- {}
-
- QScxmlStateMachine *build(DocumentModel::ScxmlDocument *doc)
- {
- m_stateMachine = Q_NULLPTR;
- m_parents.reserve(32);
- m_allTransitions.reserve(doc->allTransitions.size());
- m_docStatesToQStates.reserve(doc->allStates.size());
- m_qtMode = doc->qtMode;
-
- doc->root->accept(this);
- wireTransitions();
- applyInitialStates();
-
- QScxmlExecutableContent::DynamicTableData *td = tableData();
- td->setParent(m_stateMachine);
- m_stateMachine->setTableData(td);
- m_stateMachine->initDynamicParts(m_eventSignals, m_eventSlots, m_stateNames.keys(), m_subStateMachineNames.toList());
-
- const auto signalCode = QByteArray::number(QSIGNAL_CODE);
- for (auto it = m_stateNames.constBegin(), eit = m_stateNames.constEnd(); it != eit; ++it) {
- QByteArray signal = signalCode + it.key().toUtf8() + "Changed(bool)";
- QObject::connect(it.value(), SIGNAL(activeChanged(bool)), m_stateMachine, signal.constData());
- }
-
- m_parents.clear();
- m_allTransitions.clear();
- m_docStatesToQStates.clear();
- m_currentTransition = Q_NULLPTR;
-
- return m_stateMachine;
- }
-
-private:
- using NodeVisitor::visit;
- using QScxmlExecutableContent::Builder::createContext;
-
- bool visit(DocumentModel::Scxml *node) Q_DECL_OVERRIDE
- {
- m_stateMachine = new DynamicStateMachine;
-
- switch (node->binding) {
- case DocumentModel::Scxml::EarlyBinding:
- m_stateMachine->setDataBinding(QScxmlStateMachine::EarlyBinding);
- break;
- case DocumentModel::Scxml::LateBinding:
- m_stateMachine->setDataBinding(QScxmlStateMachine::LateBinding);
- m_bindLate = true;
- break;
- default:
- Q_UNREACHABLE();
- }
-
- setName(node->name);
-
- m_parents.append(QScxmlStateMachinePrivate::get(m_stateMachine)->m_qStateMachine);
- visit(node->children);
-
- m_dataElements.append(node->dataElements);
- if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) {
- setInitialSetup(startNewSequence());
- generate(m_dataElements);
- if (node->script) {
- node->script->accept(this);
- }
- visit(&node->initialSetup);
- endSequence();
- }
-
- m_parents.removeLast();
-
- foreach (auto initialState, node->initialStates) {
- Q_ASSERT(initialState);
- m_initialStates.append(qMakePair(QScxmlStateMachinePrivate::get(m_stateMachine)->m_qStateMachine, initialState));
- }
-
- return false;
- }
-
- bool visit(DocumentModel::State *node) Q_DECL_OVERRIDE
- {
- QAbstractState *newState = Q_NULLPTR;
- switch (node->type) {
- case DocumentModel::State::Normal: {
- auto s = new QScxmlState(currentParent());
- newState = s;
- foreach (DocumentModel::AbstractState *initialState, node->initialStates) {
- m_initialStates.append(qMakePair(s, initialState));
- }
- } break;
- case DocumentModel::State::Parallel: {
- auto s = new QScxmlState(currentParent());
- s->setChildMode(QState::ParallelStates);
- newState = s;
- } break;
- case DocumentModel::State::Initial: {
- auto s = new QScxmlState(currentParent());
- currentParent()->setInitialState(s);
- newState = s;
- } break;
- case DocumentModel::State::Final: {
- auto s = new QScxmlFinalState(currentParent());
- newState = s;
- s->setDoneData(generate(node->doneData));
- } break;
- default:
- Q_UNREACHABLE();
- }
-
- newState->setObjectName(node->id);
- m_stateNames.insert(node->id, newState);
-
- m_docStatesToQStates.insert(node, newState);
- m_parents.append(newState);
-
- if (!node->dataElements.isEmpty()) {
- if (m_bindLate) {
- qobject_cast<QScxmlState *>(newState)->setInitInstructions(startNewSequence());
- generate(node->dataElements);
- endSequence();
- } else {
- m_dataElements.append(node->dataElements);
- }
- }
-
- QScxmlExecutableContent::ContainerId onEntry = generate(node->onEntry);
- QScxmlExecutableContent::ContainerId onExit = generate(node->onExit);
- if (QScxmlState *s = qobject_cast<QScxmlState *>(newState)) {
- s->setOnEntryInstructions(onEntry);
- s->setOnExitInstructions(onExit);
- if (!node->invokes.isEmpty()) {
- QVector<QScxmlInvokableServiceFactory *> factories;
- foreach (DocumentModel::Invoke *invoke, node->invokes) {
- auto ctxt = createContext(QStringLiteral("invoke"));
- QVector<QScxmlExecutableContent::StringId> namelist;
- foreach (const QString &name, invoke->namelist)
- namelist += addString(name);
- QVector<QScxmlInvokableServiceFactory::Param> params;
- foreach (DocumentModel::Param *param, invoke->params) {
- QScxmlInvokableServiceFactory::Param p;
- p.name = addString(param->name);
- p.expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"), param->expr);
- p.location = addString(param->location);
- params.append(p);
- }
- QScxmlExecutableContent::ContainerId finalize = QScxmlExecutableContent::NoInstruction;
- if (!invoke->finalize.isEmpty()) {
- finalize = startNewSequence();
- visit(&invoke->finalize);
- endSequence();
- }
- auto factory = new InvokeDynamicScxmlFactory(ctxt,
- addString(invoke->id),
- addString(node->id + QStringLiteral(".session-")),
- addString(invoke->idLocation),
- namelist,
- invoke->autoforward,
- params,
- finalize);
- factory->setContent(invoke->content);
- factories.append(factory);
- QString name = invoke->content->root->name;
- if (!name.isEmpty()) {
- m_subStateMachineNames.insert(name);
- }
- }
- s->setInvokableServiceFactories(factories);
- }
- } else if (QScxmlFinalState *f = qobject_cast<QScxmlFinalState *>(newState)) {
- f->setOnEntryInstructions(onEntry);
- f->setOnExitInstructions(onExit);
- } else {
- Q_UNREACHABLE();
- }
-
- visit(node->children);
-
- m_parents.removeLast();
- return false;
- }
-
- bool visit(DocumentModel::Transition *node) Q_DECL_OVERRIDE
- {
- if (m_qtMode) {
- m_eventSlots.unite(node->events.toSet());
- }
-
- auto newTransition = new QScxmlTransition(node->events);
- if (QHistoryState *parent = qobject_cast<QHistoryState*>(m_parents.last())) {
- parent->setDefaultTransition(newTransition);
- } else {
- currentParent()->addTransition(newTransition);
- }
-
- if (node->condition) {
- auto cond = createEvaluatorBool(QStringLiteral("transition"), QStringLiteral("cond"), *node->condition.data());
- newTransition->setConditionalExpression(cond);
- }
-
- switch (node->type) {
- case DocumentModel::Transition::External:
- newTransition->setTransitionType(QAbstractTransition::ExternalTransition);
- break;
- case DocumentModel::Transition::Internal:
- newTransition->setTransitionType(QAbstractTransition::InternalTransition);
- break;
- default:
- Q_UNREACHABLE();
- }
-
- m_allTransitions.insert(newTransition, node);
- if (!node->instructionsOnTransition.isEmpty()) {
- m_currentTransition = newTransition;
- newTransition->setInstructionsOnTransition(startNewSequence());
- visit(&node->instructionsOnTransition);
- endSequence();
- m_currentTransition = 0;
- }
- Q_ASSERT(newTransition->stateMachine());
- return false;
- }
-
- bool visit(DocumentModel::HistoryState *state) Q_DECL_OVERRIDE
- {
- QHistoryState *newState = new QScxmlHistoryState(currentParent());
- switch (state->type) {
- case DocumentModel::HistoryState::Shallow:
- newState->setHistoryType(QHistoryState::ShallowHistory);
- break;
- case DocumentModel::HistoryState::Deep:
- newState->setHistoryType(QHistoryState::DeepHistory);
- break;
- default:
- Q_UNREACHABLE();
- }
-
- newState->setObjectName(state->id);
- m_docStatesToQStates.insert(state, newState);
- m_parents.append(newState);
- return true;
- }
+ bool ok = true;
+ auto srcexpr = d->calculateSrcexpr(parentStateMachine, &ok);
+ if (!ok)
+ return Q_NULLPTR;
- void endVisit(DocumentModel::HistoryState *) Q_DECL_OVERRIDE
- {
- m_parents.removeLast();
- }
+ if (!srcexpr.isEmpty())
+ return invokeDynamic(parentStateMachine, srcexpr);
- bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE
- {
- if (m_qtMode && node->type == QStringLiteral("qt:signal")) {
- m_eventSignals.insert(node->event);
- }
+ auto childStateMachine = DynamicStateMachine::build(m_content.data());
- return QScxmlExecutableContent::Builder::visit(node);
- }
+ auto dm = QScxmlDataModelPrivate::instantiateDataModel(m_content->root->dataModel);
+ dm->setParent(childStateMachine);
+ childStateMachine->setDataModel(dm);
-private: // Utility methods
- QState *currentParent() const
- {
- if (m_parents.isEmpty())
- return Q_NULLPTR;
+ return invokeStatic(childStateMachine, parentStateMachine);
+}
+#endif // BUILD_QSCXMLC
- QState *parent = qobject_cast<QState*>(m_parents.last());
- Q_ASSERT(parent);
- return parent;
- }
+} // anonymous namespace
- void wireTransitions()
- {
- for (QHash<QAbstractTransition *, DocumentModel::Transition*>::const_iterator i = m_allTransitions.begin(), ei = m_allTransitions.end(); i != ei; ++i) {
- QList<QAbstractState *> targets;
- targets.reserve(i.value()->targets.size());
- foreach (DocumentModel::AbstractState *targetState, i.value()->targetStates) {
- QAbstractState *target = m_docStatesToQStates.value(targetState);
- Q_ASSERT(target);
- targets.append(target);
- }
- i.key()->setTargetStates(targets);
+#ifndef BUILD_QSCXMLC
+QScxmlScxmlService *QScxmlScxmlServiceFactory::invokeDynamic(
+ QScxmlStateMachine *parentStateMachine, const QString &sourceUrl)
+{
+ QScxmlCompiler::Loader *loader = parentStateMachine->loader();
- if (DebugHelper_NameTransitions)
- i.key()->setObjectName(QStringLiteral("%1 -> %2").arg(i.key()->parent()->objectName(), i.value()->targets.join(QStringLiteral(","))));
- }
- }
+ const QString baseDir = sourceUrl.isEmpty() ? QString() : QFileInfo(sourceUrl).path();
+ QStringList errs;
+ const QByteArray data = loader->load(sourceUrl, baseDir, &errs);
- void applyInitialStates()
- {
- foreach (const auto &init, m_initialStates) {
- Q_ASSERT(init.second);
- auto initialState = m_docStatesToQStates.value(init.second);
- Q_ASSERT(initialState);
- init.first->setInitialState(initialState);
- }
+ if (!errs.isEmpty()) {
+ qWarning() << errs;
+ return Q_NULLPTR;
}
- QString createContextString(const QString &instrName) const Q_DECL_OVERRIDE
- {
- if (m_currentTransition) {
- QString state;
- if (QState *s = m_currentTransition->sourceState()) {
- state = QStringLiteral(" of state '%1'").arg(s->objectName());
- }
- return QStringLiteral("%1 instruction in transition %2 %3").arg(instrName, m_currentTransition->objectName(), state);
- } else {
- return QStringLiteral("%1 instruction in state %2").arg(instrName, m_parents.last()->objectName());
- }
+ QXmlStreamReader reader(data);
+ QScxmlCompiler compiler(&reader);
+ compiler.setFileName(sourceUrl);
+ compiler.setLoader(parentStateMachine->loader());
+ compiler.compile();
+ if (!compiler.errors().isEmpty()) {
+ const auto errors = compiler.errors();
+ for (const QScxmlError &error : errors)
+ qWarning() << error.toString();
+ return Q_NULLPTR;
}
- QString createContext(const QString &instrName, const QString &attrName, const QString &attrValue) const Q_DECL_OVERRIDE
- {
- QString location = createContextString(instrName);
- return QStringLiteral("%1 with %2=\"%3\"").arg(location, attrName, attrValue);
+ auto mainDoc = QScxmlCompilerPrivate::get(&compiler)->scxmlDocument();
+ if (mainDoc == nullptr) {
+ Q_ASSERT(!compiler.errors().isEmpty());
+ const auto errors = compiler.errors();
+ for (const QScxmlError &error : errors)
+ qWarning() << error.toString();
+ return Q_NULLPTR;
}
-private:
- DynamicStateMachine *m_stateMachine;
- QVector<QAbstractState *> m_parents;
- QHash<QAbstractTransition *, DocumentModel::Transition*> m_allTransitions;
- QHash<DocumentModel::AbstractState *, QAbstractState *> m_docStatesToQStates;
- QAbstractTransition *m_currentTransition;
- QVector<QPair<QState *, DocumentModel::AbstractState *>> m_initialStates;
- bool m_bindLate;
- bool m_qtMode;
- QVector<DocumentModel::DataElement *> m_dataElements;
- QSet<QString> m_eventSignals;
- QSet<QString> m_eventSlots;
- QHash<QString, QAbstractState *> m_stateNames;
- QSet<QString> m_subStateMachineNames;
-};
-
-inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke(QScxmlStateMachine *parent)
-{
- auto child = QStateMachineBuilder().build(m_content.data());
+ auto childStateMachine = DynamicStateMachine::build(mainDoc);
- auto dm = QScxmlDataModelPrivate::instantiateDataModel(m_content->root->dataModel);
- dm->setParent(child);
- child->setDataModel(dm);
+ auto dm = QScxmlDataModelPrivate::instantiateDataModel(mainDoc->root->dataModel);
+ dm->setParent(childStateMachine);
+ childStateMachine->setDataModel(dm);
- return finishInvoke(child, parent);
+ return invokeStatic(childStateMachine, parentStateMachine);
}
#endif // BUILD_QSCXMLC
-} // anonymous namespace
-
/*!
- * \class QScxmlParser
- * \brief The QScxmlParser class is a parser for SCXML files.
+ * \class QScxmlCompiler
+ * \brief The QScxmlCompiler class is a compiler for SCXML files.
* \since 5.7
* \inmodule QtScxml
*
- * Parses an \l{SCXML Specification}{SCXML} file. It can also dynamically instantiate a
- * state machine for a successfully parsed SCXML file. If parsing failed and
- * instantiateStateMachine() is called, the new state machine cannot start. All errors are
- * returned by QScxmlStateMachine::parseErrors().
+ * Parses an \l{SCXML Specification}{SCXML} file and dynamically instantiates a
+ * state machine for a successfully parsed SCXML file. If parsing fails, the
+ * new state machine cannot start. All errors are returned by
+ * QScxmlStateMachine::parseErrors().
*
* To load an SCXML file, QScxmlStateMachine::fromFile or QScxmlStateMachine::fromData should be
- * used. Using QScxmlParser directly is only needed when the parser needs to use a custom
- * QScxmlParser::Loader.
- */
-
-/*!
- \enum QScxmlParser::QtMode
-
- This enum specifies if the document should be parsed in Qt mode. In Qt
- mode, event and state names have to be valid C++ identifiers. If that is
- the case some additional convenience methods are generated. If not, the
- parser will reject the document. Qt mode can be enabled in the document
- itself by adding an XML comment of the form:
-
- \c {<!-- enable-qt-mode: yes -->}
-
- \value QtModeDisabled
- Ignore the XML comment and do not generate additional methods.
- \value QtModeEnabled
- Force parsing in Qt mode and try to generate the additional methods,
- no matter if the XML comment is present.
- \value QtModeFromInputFile
- Enable Qt mode only if the XML comment is present in the document.
+ * used. Using QScxmlCompiler directly is only needed when the compiler needs to use a custom
+ * QScxmlCompiler::Loader.
*/
/*!
- * Creates a new SCXML parser for the specified \a reader.
+ * Creates a new SCXML compiler for the specified \a reader.
*/
-QScxmlParser::QScxmlParser(QXmlStreamReader *reader)
- : d(new QScxmlParserPrivate(this, reader))
+QScxmlCompiler::QScxmlCompiler(QXmlStreamReader *reader)
+ : d(new QScxmlCompilerPrivate(reader))
{ }
/*!
- * Destroys the SCXML parser.
+ * Destroys the SCXML compiler.
*/
-QScxmlParser::~QScxmlParser()
+QScxmlCompiler::~QScxmlCompiler()
{
delete d;
}
@@ -1431,7 +748,7 @@ QScxmlParser::~QScxmlParser()
*
* \sa setFileName()
*/
-QString QScxmlParser::fileName() const
+QString QScxmlCompiler::fileName() const
{
return d->fileName();
}
@@ -1443,82 +760,98 @@ QString QScxmlParser::fileName() const
*
* \sa fileName()
*/
-void QScxmlParser::setFileName(const QString &fileName)
+void QScxmlCompiler::setFileName(const QString &fileName)
{
d->setFileName(fileName);
}
/*!
- * Returns the loader that is currently used to resolve and load URIs.
+ * Returns the loader that is currently used to resolve and load URIs for the
+ * SCXML compiler.
*
* \sa setLoader()
*/
-QScxmlParser::Loader *QScxmlParser::loader() const
+QScxmlCompiler::Loader *QScxmlCompiler::loader() const
{
return d->loader();
}
/*!
- * Sets \a newLoader to be used for resolving and loading URIs.
+ * Sets \a newLoader to be used for resolving and loading URIs for the SCXML
+ * compiler.
*
* \sa loader()
*/
-void QScxmlParser::setLoader(QScxmlParser::Loader *newLoader)
+void QScxmlCompiler::setLoader(QScxmlCompiler::Loader *newLoader)
{
d->setLoader(newLoader);
}
/*!
- * Parses an SCXML file.
+ * Parses an SCXML file and creates a new state machine from it.
+ *
+ * If parsing is successful, the returned state machine can be initialized and started. If
+ * parsing fails, QScxmlStateMachine::parseErrors() can be used to retrieve a list of errors.
*/
-void QScxmlParser::parse()
+QScxmlStateMachine *QScxmlCompiler::compile()
{
d->readDocument();
- d->verifyDocument();
+ if (d->errors().isEmpty()) {
+ // Only verify the document if there were no parse errors: if there were any, the document
+ // is incomplete and will contain errors for sure. There is no need to heap more errors on
+ // top of other errors.
+ d->verifyDocument();
+ }
+ return d->instantiateStateMachine();
}
/*!
+ * \internal
* Instantiates a new state machine from the parsed SCXML.
*
* If parsing is successful, the returned state machine can be initialized and started. If
* parsing fails, QScxmlStateMachine::parseErrors() can be used to retrieve a list of errors.
*
* \note The instantiated state machine will not have an associated data model set.
- * \sa QScxmlParser::instantiateDataModel
+ * \sa QScxmlCompilerPrivate::instantiateDataModel
*/
-QScxmlStateMachine *QScxmlParser::instantiateStateMachine() const
+QScxmlStateMachine *QScxmlCompilerPrivate::instantiateStateMachine() const
{
#ifdef BUILD_QSCXMLC
return Q_NULLPTR;
#else // BUILD_QSCXMLC
- DocumentModel::ScxmlDocument *doc = d->scxmlDocument();
+ DocumentModel::ScxmlDocument *doc = scxmlDocument();
if (doc && doc->root) {
- return QStateMachineBuilder().build(doc);
+ auto stateMachine = DynamicStateMachine::build(doc);
+ instantiateDataModel(stateMachine);
+ return stateMachine;
} else {
class InvalidStateMachine: public QScxmlStateMachine {
public:
- InvalidStateMachine()
+ InvalidStateMachine() : QScxmlStateMachine(&QScxmlStateMachine::staticMetaObject)
{}
};
auto stateMachine = new InvalidStateMachine;
QScxmlStateMachinePrivate::get(stateMachine)->parserData()->m_errors = errors();
+ instantiateDataModel(stateMachine);
return stateMachine;
}
#endif // BUILD_QSCXMLC
}
/*!
+ * \internal
* Instantiates the data model as described in the SCXML file.
*
* After instantiation, the \a stateMachine takes ownership of the data model.
*/
-void QScxmlParser::instantiateDataModel(QScxmlStateMachine *stateMachine) const
+void QScxmlCompilerPrivate::instantiateDataModel(QScxmlStateMachine *stateMachine) const
{
#ifdef BUILD_QSCXMLC
Q_UNUSED(stateMachine)
#else
- auto doc = d->scxmlDocument();
+ auto doc = scxmlDocument();
auto root = doc ? doc->root : Q_NULLPTR;
if (root == Q_NULLPTR) {
qWarning() << "SCXML document has no root element";
@@ -1535,48 +868,12 @@ void QScxmlParser::instantiateDataModel(QScxmlStateMachine *stateMachine) const
/*!
* Returns the list of parse errors.
*/
-QVector<QScxmlError> QScxmlParser::errors() const
+QVector<QScxmlError> QScxmlCompiler::errors() const
{
return d->errors();
}
-/*!
- * Adds the error message \a msg.
- *
- * The line and column numbers for the error message are the current line and
- * column numbers of the QXmlStreamReader.
- */
-void QScxmlParser::addError(const QString &msg)
-{
- d->addError(msg);
-}
-
-/*!
- * Returns how the parser decides if the SCXML document should conform to Qt
- * mode.
- *
- * \sa QtMode
- */
-QScxmlParser::QtMode QScxmlParser::qtMode() const
-{
- return d->qtMode();
-}
-
-/*!
- * Sets the \c qtMode to \a mode. This property overrides the XML comment. You
- * can force Qt mode to be used by setting it to \c QtModeEnabled or force any
- * XML comments to be ignored and Qt mode to be used by setting it to
- * \c QtModeDisabled. The default is \c QtModeFromInputFile, which will switch
- * Qt mode on if the XML comment is present in the source file.
- *
- * \sa QtMode
- */
-void QScxmlParser::setQtMode(QScxmlParser::QtMode mode)
-{
- d->setQtMode(mode);
-}
-
-bool QScxmlParserPrivate::ParserState::collectChars() {
+bool QScxmlCompilerPrivate::ParserState::collectChars() {
switch (kind) {
case Content:
case Data:
@@ -1588,11 +885,11 @@ bool QScxmlParserPrivate::ParserState::collectChars() {
return false;
}
-bool QScxmlParserPrivate::ParserState::validChild(ParserState::Kind child) const {
+bool QScxmlCompilerPrivate::ParserState::validChild(ParserState::Kind child) const {
return validChild(kind, child);
}
-bool QScxmlParserPrivate::ParserState::validChild(ParserState::Kind parent, ParserState::Kind child)
+bool QScxmlCompilerPrivate::ParserState::validChild(ParserState::Kind parent, ParserState::Kind child)
{
switch (parent) {
case ParserState::Scxml:
@@ -1697,7 +994,7 @@ bool QScxmlParserPrivate::ParserState::validChild(ParserState::Kind parent, Pars
return false;
}
-bool QScxmlParserPrivate::ParserState::isExecutableContent(ParserState::Kind kind) {
+bool QScxmlCompilerPrivate::ParserState::isExecutableContent(ParserState::Kind kind) {
switch (kind) {
case Raise:
case Send:
@@ -1715,7 +1012,7 @@ bool QScxmlParserPrivate::ParserState::isExecutableContent(ParserState::Kind kin
return false;
}
-QScxmlParserPrivate::ParserState::Kind QScxmlParserPrivate::ParserState::nameToParserStateKind(const QStringRef &name)
+QScxmlCompilerPrivate::ParserState::Kind QScxmlCompilerPrivate::ParserState::nameToParserStateKind(const QStringRef &name)
{
static QMap<QString, ParserState::Kind> nameToKind;
if (nameToKind.isEmpty()) {
@@ -1756,7 +1053,7 @@ QScxmlParserPrivate::ParserState::Kind QScxmlParserPrivate::ParserState::nameToP
return None;
}
-QStringList QScxmlParserPrivate::ParserState::requiredAttributes(QScxmlParserPrivate::ParserState::Kind kind)
+QStringList QScxmlCompilerPrivate::ParserState::requiredAttributes(QScxmlCompilerPrivate::ParserState::Kind kind)
{
switch (kind) {
case Scxml: return QStringList() << QStringLiteral("version");
@@ -1791,7 +1088,7 @@ QStringList QScxmlParserPrivate::ParserState::requiredAttributes(QScxmlParserPri
return QStringList();
}
-QStringList QScxmlParserPrivate::ParserState::optionalAttributes(QScxmlParserPrivate::ParserState::Kind kind)
+QStringList QScxmlCompilerPrivate::ParserState::optionalAttributes(QScxmlCompilerPrivate::ParserState::Kind kind)
{
switch (kind) {
case Scxml: return QStringList() << QStringLiteral("initial")
@@ -1858,6 +1155,15 @@ DocumentModel::Node::~Node()
{
}
+DocumentModel::AbstractState *DocumentModel::Node::asAbstractState()
+{
+ if (State *state = asState())
+ return state;
+ if (HistoryState *history = asHistoryState())
+ return history;
+ return Q_NULLPTR;
+}
+
void DocumentModel::DataElement::accept(DocumentModel::NodeVisitor *visitor)
{
visitor->visit(this);
@@ -1871,7 +1177,7 @@ void DocumentModel::Param::accept(DocumentModel::NodeVisitor *visitor)
void DocumentModel::DoneData::accept(DocumentModel::NodeVisitor *visitor)
{
if (visitor->visit(this)) {
- foreach (Param *param, params)
+ for (Param *param : qAsConst(params))
param->accept(visitor);
}
visitor->endVisit(this);
@@ -1944,7 +1250,7 @@ void DocumentModel::State::accept(DocumentModel::NodeVisitor *visitor)
visitor->visit(onExit);
if (doneData)
doneData->accept(visitor);
- foreach (Invoke *invoke, invokes)
+ for (Invoke *invoke : qAsConst(invokes))
invoke->accept(visitor);
}
visitor->endVisit(this);
@@ -1982,69 +1288,47 @@ void DocumentModel::Scxml::accept(DocumentModel::NodeVisitor *visitor)
DocumentModel::NodeVisitor::~NodeVisitor()
{}
-bool DocumentModel::isValidQPropertyName(const QString &str)
-{
- return !str.isEmpty() && !str.contains(QLatin1Char('(')) && !str.contains(QLatin1Char(')'));
-}
-
-bool DocumentModel::isEventToBeGenerated(const QString &event)
-{
- return !event.contains(QLatin1Char('.'));
-}
-
/*!
- * \class QScxmlParser::Loader
- * \brief The Loader class is a URI resolver and resource loader for an SCXML parser.
- * \since 5.7
+ * \class QScxmlCompiler::Loader
+ * \brief The Loader class is a URI resolver and resource loader for an SCXML compiler.
+ * \since 5.8
* \inmodule QtScxml
*/
/*!
- * Creates a new loader for the specified \a parser.
+ * Creates a new loader.
*/
-QScxmlParser::Loader::Loader(QScxmlParser *parser)
- : m_parser(parser)
+QScxmlCompiler::Loader::Loader()
{
- Q_ASSERT(parser);
}
/*!
* Destroys the loader.
*/
-QScxmlParser::Loader::~Loader()
+QScxmlCompiler::Loader::~Loader()
{}
/*!
- * Returns the parser that is associated with this loader.
- */
-QScxmlParser *QScxmlParser::Loader::parser() const
-{
- return m_parser;
-}
-
-/*!
- * \fn QScxmlParser::Loader::load(const QString &name, const QString &baseDir, bool *ok)
+ * \fn QScxmlCompiler::Loader::load(const QString &name, const QString &baseDir, QStringList *errors)
* Resolves the URI \a name and loads an SCXML file from the directory
- * specified by \a baseDir. The boolean parameter \a ok indicates whether
- * the loading was successful.
+ * specified by \a baseDir. \a errors contains information about the errors that
+ * might have occurred.
*
* Returns a QByteArray that stores the contents of the file.
*/
-QScxmlParserPrivate *QScxmlParserPrivate::get(QScxmlParser *parser)
+QScxmlCompilerPrivate *QScxmlCompilerPrivate::get(QScxmlCompiler *compiler)
{
- return parser->d;
+ return compiler->d;
}
-QScxmlParserPrivate::QScxmlParserPrivate(QScxmlParser *parser, QXmlStreamReader *reader)
+QScxmlCompilerPrivate::QScxmlCompilerPrivate(QXmlStreamReader *reader)
: m_currentState(Q_NULLPTR)
- , m_defaultLoader(parser)
, m_loader(&m_defaultLoader)
, m_reader(reader)
- , m_qtMode(QScxmlParser::QtModeFromInputFile)
{}
-bool QScxmlParserPrivate::verifyDocument()
+bool QScxmlCompilerPrivate::verifyDocument()
{
if (!m_doc)
return false;
@@ -2059,65 +1343,60 @@ bool QScxmlParserPrivate::verifyDocument()
return false;
}
-DocumentModel::ScxmlDocument *QScxmlParserPrivate::scxmlDocument() const
+DocumentModel::ScxmlDocument *QScxmlCompilerPrivate::scxmlDocument() const
{
return m_doc && m_errors.isEmpty() ? m_doc.data() : Q_NULLPTR;
}
-QString QScxmlParserPrivate::fileName() const
+QString QScxmlCompilerPrivate::fileName() const
{
return m_fileName;
}
-void QScxmlParserPrivate::setFileName(const QString &fileName)
+void QScxmlCompilerPrivate::setFileName(const QString &fileName)
{
m_fileName = fileName;
}
-QScxmlParser::Loader *QScxmlParserPrivate::loader() const
+QScxmlCompiler::Loader *QScxmlCompilerPrivate::loader() const
{
return m_loader;
}
-void QScxmlParserPrivate::setLoader(QScxmlParser::Loader *loader)
+void QScxmlCompilerPrivate::setLoader(QScxmlCompiler::Loader *loader)
{
m_loader = loader;
}
-void QScxmlParserPrivate::parseSubDocument(DocumentModel::Invoke *parentInvoke, QXmlStreamReader *reader, const QString &fileName)\
+void QScxmlCompilerPrivate::parseSubDocument(DocumentModel::Invoke *parentInvoke,
+ QXmlStreamReader *reader,
+ const QString &fileName)
{
- QScxmlParser p(reader);
+ QScxmlCompiler p(reader);
p.setFileName(fileName);
+ p.setLoader(loader());
p.d->readDocument();
parentInvoke->content.reset(p.d->m_doc.take());
m_doc->allSubDocuments.append(parentInvoke->content.data());
m_errors.append(p.errors());
}
-bool QScxmlParserPrivate::parseSubElement(DocumentModel::Invoke *parentInvoke, QXmlStreamReader *reader, const QString &fileName)
+bool QScxmlCompilerPrivate::parseSubElement(DocumentModel::Invoke *parentInvoke,
+ QXmlStreamReader *reader,
+ const QString &fileName)
{
- QScxmlParser p(reader);
+ QScxmlCompiler p(reader);
p.setFileName(fileName);
+ p.setLoader(loader());
p.d->resetDocument();
bool ok = p.d->readElement();
parentInvoke->content.reset(p.d->m_doc.take());
m_doc->allSubDocuments.append(parentInvoke->content.data());
m_errors.append(p.errors());
- parentInvoke->content->qtMode = m_doc->qtMode;
return ok;
}
-static bool isWordEnd(const QStringRef &str, int start)
-{
- if (str.size() <= start) {
- return true;
- }
-
- QChar ch = str.at(start);
- return ch.isSpace();
-}
-
-bool QScxmlParserPrivate::preReadElementScxml()
+bool QScxmlCompilerPrivate::preReadElementScxml()
{
if (m_doc->root) {
addError(QLatin1String("Doc root already allocated"));
@@ -2176,7 +1455,7 @@ bool QScxmlParserPrivate::preReadElementScxml()
}
-bool QScxmlParserPrivate::preReadElementState()
+bool QScxmlCompilerPrivate::preReadElementState()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto newState = m_doc->newState(m_currentState, DocumentModel::State::Normal, xmlLocation());
@@ -2191,7 +1470,7 @@ bool QScxmlParserPrivate::preReadElementState()
return true;
}
-bool QScxmlParserPrivate::preReadElementParallel()
+bool QScxmlCompilerPrivate::preReadElementParallel()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto newState = m_doc->newState(m_currentState, DocumentModel::State::Parallel, xmlLocation());
@@ -2202,7 +1481,7 @@ bool QScxmlParserPrivate::preReadElementParallel()
return true;
}
-bool QScxmlParserPrivate::preReadElementInitial()
+bool QScxmlCompilerPrivate::preReadElementInitial()
{
DocumentModel::AbstractState *parent = currentParent();
if (!parent) {
@@ -2220,15 +1499,36 @@ bool QScxmlParserPrivate::preReadElementInitial()
addError(QStringLiteral("Explicit initial state for parallel states not supported (only implicitly through the initial states of its substates)"));
return false;
}
- auto newState = m_doc->newState(m_currentState, DocumentModel::State::Initial, xmlLocation());
- m_currentState = newState;
return true;
}
-bool QScxmlParserPrivate::preReadElementTransition()
-{
+bool QScxmlCompilerPrivate::preReadElementTransition()
+{
+ // Parser stack at this point:
+ // <transition>
+ // <initial>
+ // <state> or <scxml>
+ //
+ // Or:
+ // <transition>
+ // <state> or <scxml>
+
+ DocumentModel::Transition *transition = nullptr;
+ if (previous().kind == ParserState::Initial) {
+ transition = m_doc->newTransition(nullptr, xmlLocation());
+ const auto &initialParentState = m_stack.at(m_stack.size() - 3);
+ if (initialParentState.kind == ParserState::Scxml) {
+ m_currentState->asScxml()->initialTransition = transition;
+ } else if (initialParentState.kind == ParserState::State) {
+ m_currentState->asState()->initialTransition = transition;
+ } else {
+ Q_UNREACHABLE();
+ }
+ } else {
+ transition = m_doc->newTransition(m_currentState, xmlLocation());
+ }
+
const QXmlStreamAttributes attributes = m_reader->attributes();
- auto transition = m_doc->newTransition(m_currentState, xmlLocation());
transition->events = attributes.value(QLatin1String("event")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
transition->targets = attributes.value(QLatin1String("target")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
if (attributes.hasAttribute(QStringLiteral("cond")))
@@ -2246,7 +1546,7 @@ bool QScxmlParserPrivate::preReadElementTransition()
return true;
}
-bool QScxmlParserPrivate::preReadElementFinal()
+bool QScxmlCompilerPrivate::preReadElementFinal()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto newState = m_doc->newState(m_currentState, DocumentModel::State::Final, xmlLocation());
@@ -2256,7 +1556,7 @@ bool QScxmlParserPrivate::preReadElementFinal()
return true;
}
-bool QScxmlParserPrivate::preReadElementHistory()
+bool QScxmlCompilerPrivate::preReadElementHistory()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
@@ -2282,7 +1582,7 @@ bool QScxmlParserPrivate::preReadElementHistory()
return true;
}
-bool QScxmlParserPrivate::preReadElementOnEntry()
+bool QScxmlCompilerPrivate::preReadElementOnEntry()
{
const ParserState::Kind previousKind = previous().kind;
switch (previousKind) {
@@ -2301,7 +1601,7 @@ bool QScxmlParserPrivate::preReadElementOnEntry()
return true;
}
-bool QScxmlParserPrivate::preReadElementOnExit()
+bool QScxmlCompilerPrivate::preReadElementOnExit()
{
ParserState::Kind previousKind = previous().kind;
switch (previousKind) {
@@ -2320,7 +1620,7 @@ bool QScxmlParserPrivate::preReadElementOnExit()
return true;
}
-bool QScxmlParserPrivate::preReadElementRaise()
+bool QScxmlCompilerPrivate::preReadElementRaise()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto raise = m_doc->newNode<DocumentModel::Raise>(xmlLocation());
@@ -2329,7 +1629,7 @@ bool QScxmlParserPrivate::preReadElementRaise()
return true;
}
-bool QScxmlParserPrivate::preReadElementIf()
+bool QScxmlCompilerPrivate::preReadElementIf()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto *ifI = m_doc->newNode<DocumentModel::If>(xmlLocation());
@@ -2339,7 +1639,7 @@ bool QScxmlParserPrivate::preReadElementIf()
return true;
}
-bool QScxmlParserPrivate::preReadElementElseIf()
+bool QScxmlCompilerPrivate::preReadElementElseIf()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
@@ -2352,7 +1652,7 @@ bool QScxmlParserPrivate::preReadElementElseIf()
return true;
}
-bool QScxmlParserPrivate::preReadElementElse()
+bool QScxmlCompilerPrivate::preReadElementElse()
{
DocumentModel::If *ifI = lastIf();
if (!ifI)
@@ -2362,7 +1662,7 @@ bool QScxmlParserPrivate::preReadElementElse()
return true;
}
-bool QScxmlParserPrivate::preReadElementForeach()
+bool QScxmlCompilerPrivate::preReadElementForeach()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto foreachI = m_doc->newNode<DocumentModel::Foreach>(xmlLocation());
@@ -2374,7 +1674,7 @@ bool QScxmlParserPrivate::preReadElementForeach()
return true;
}
-bool QScxmlParserPrivate::preReadElementLog()
+bool QScxmlCompilerPrivate::preReadElementLog()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto logI = m_doc->newNode<DocumentModel::Log>(xmlLocation());
@@ -2384,12 +1684,12 @@ bool QScxmlParserPrivate::preReadElementLog()
return true;
}
-bool QScxmlParserPrivate::preReadElementDataModel()
+bool QScxmlCompilerPrivate::preReadElementDataModel()
{
return true;
}
-bool QScxmlParserPrivate::preReadElementData()
+bool QScxmlCompilerPrivate::preReadElementData()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto data = m_doc->newNode<DocumentModel::DataElement>(xmlLocation());
@@ -2406,7 +1706,7 @@ bool QScxmlParserPrivate::preReadElementData()
return true;
}
-bool QScxmlParserPrivate::preReadElementAssign()
+bool QScxmlCompilerPrivate::preReadElementAssign()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto assign = m_doc->newNode<DocumentModel::Assign>(xmlLocation());
@@ -2416,7 +1716,7 @@ bool QScxmlParserPrivate::preReadElementAssign()
return true;
}
-bool QScxmlParserPrivate::preReadElementDoneData()
+bool QScxmlCompilerPrivate::preReadElementDoneData()
{
DocumentModel::State *s = m_currentState->asState();
if (s && s->type == DocumentModel::State::Final) {
@@ -2431,7 +1731,7 @@ bool QScxmlParserPrivate::preReadElementDoneData()
return true;
}
-bool QScxmlParserPrivate::preReadElementContent()
+bool QScxmlCompilerPrivate::preReadElementContent()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
ParserState::Kind previousKind = previous().kind;
@@ -2444,7 +1744,7 @@ bool QScxmlParserPrivate::preReadElementContent()
case ParserState::Send: {
DocumentModel::Send *s = previous().instruction->asSend();
Q_ASSERT(s);
- s->content = attributes.value(QLatin1String("expr")).toString();
+ s->contentexpr = attributes.value(QLatin1String("expr")).toString();
} break;
case ParserState::Invoke: {
DocumentModel::Invoke *i = previous().instruction->asInvoke();
@@ -2461,7 +1761,7 @@ bool QScxmlParserPrivate::preReadElementContent()
return true;
}
-bool QScxmlParserPrivate::preReadElementParam()
+bool QScxmlCompilerPrivate::preReadElementParam()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto param = m_doc->newNode<DocumentModel::Param>(xmlLocation());
@@ -2493,7 +1793,7 @@ bool QScxmlParserPrivate::preReadElementParam()
return true;
}
-bool QScxmlParserPrivate::preReadElementScript()
+bool QScxmlCompilerPrivate::preReadElementScript()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto *script = m_doc->newNode<DocumentModel::Script>(xmlLocation());
@@ -2502,7 +1802,7 @@ bool QScxmlParserPrivate::preReadElementScript()
return true;
}
-bool QScxmlParserPrivate::preReadElementSend()
+bool QScxmlCompilerPrivate::preReadElementSend()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto *send = m_doc->newNode<DocumentModel::Send>(xmlLocation());
@@ -2522,7 +1822,7 @@ bool QScxmlParserPrivate::preReadElementSend()
return true;
}
-bool QScxmlParserPrivate::preReadElementCancel()
+bool QScxmlCompilerPrivate::preReadElementCancel()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
auto *cancel = m_doc->newNode<DocumentModel::Cancel>(xmlLocation());
@@ -2532,7 +1832,7 @@ bool QScxmlParserPrivate::preReadElementCancel()
return true;
}
-bool QScxmlParserPrivate::preReadElementInvoke()
+bool QScxmlCompilerPrivate::preReadElementInvoke()
{
const QXmlStreamAttributes attributes = m_reader->attributes();
DocumentModel::State *parentState = m_currentState->asState();
@@ -2563,7 +1863,7 @@ bool QScxmlParserPrivate::preReadElementInvoke()
return true;
}
-bool QScxmlParserPrivate::preReadElementFinalize()
+bool QScxmlCompilerPrivate::preReadElementFinalize()
{
auto instr = previous().instruction;
if (!instr) {
@@ -2579,98 +1879,97 @@ bool QScxmlParserPrivate::preReadElementFinalize()
return true;
}
-bool QScxmlParserPrivate::postReadElementScxml()
+bool QScxmlCompilerPrivate::postReadElementScxml()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementState()
+bool QScxmlCompilerPrivate::postReadElementState()
{
currentStateUp();
return true;
}
-bool QScxmlParserPrivate::postReadElementParallel()
+bool QScxmlCompilerPrivate::postReadElementParallel()
{
currentStateUp();
return true;
}
-bool QScxmlParserPrivate::postReadElementInitial()
+bool QScxmlCompilerPrivate::postReadElementInitial()
{
- currentStateUp();
return true;
}
-bool QScxmlParserPrivate::postReadElementTransition()
+bool QScxmlCompilerPrivate::postReadElementTransition()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementFinal()
+bool QScxmlCompilerPrivate::postReadElementFinal()
{
currentStateUp();
return true;
}
-bool QScxmlParserPrivate::postReadElementHistory()
+bool QScxmlCompilerPrivate::postReadElementHistory()
{
currentStateUp();
return true;
}
-bool QScxmlParserPrivate::postReadElementOnEntry()
+bool QScxmlCompilerPrivate::postReadElementOnEntry()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementOnExit()
+bool QScxmlCompilerPrivate::postReadElementOnExit()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementRaise()
+bool QScxmlCompilerPrivate::postReadElementRaise()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementIf()
+bool QScxmlCompilerPrivate::postReadElementIf()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementElseIf()
+bool QScxmlCompilerPrivate::postReadElementElseIf()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementElse()
+bool QScxmlCompilerPrivate::postReadElementElse()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementForeach()
+bool QScxmlCompilerPrivate::postReadElementForeach()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementLog()
+bool QScxmlCompilerPrivate::postReadElementLog()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementDataModel()
+bool QScxmlCompilerPrivate::postReadElementDataModel()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementData()
+bool QScxmlCompilerPrivate::postReadElementData()
{
const ParserState parserState = current();
DocumentModel::DataElement *data = Q_NULLPTR;
if (auto state = m_currentState->asState()) {
data = state->dataElements.last();
- } else if (auto scxml = m_currentState->asNode()->asScxml()) {
+ } else if (auto scxml = m_currentState->asScxml()) {
data = scxml->dataElements.last();
} else {
Q_UNREACHABLE();
@@ -2711,17 +2010,17 @@ bool QScxmlParserPrivate::postReadElementData()
return true;
}
-bool QScxmlParserPrivate::postReadElementAssign()
+bool QScxmlCompilerPrivate::postReadElementAssign()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementDoneData()
+bool QScxmlCompilerPrivate::postReadElementDoneData()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementContent()
+bool QScxmlCompilerPrivate::postReadElementContent()
{
const ParserState parserState = current();
if (!parserState.chars.trimmed().isEmpty()) {
@@ -2740,12 +2039,12 @@ bool QScxmlParserPrivate::postReadElementContent()
return true;
}
-bool QScxmlParserPrivate::postReadElementParam()
+bool QScxmlCompilerPrivate::postReadElementParam()
{
return true;
}
-bool QScxmlParserPrivate::postReadElementScript()
+bool QScxmlCompilerPrivate::postReadElementScript()
{
const ParserState parserState = current();
DocumentModel::Script *scriptI = parserState.instruction->asScript();
@@ -2769,17 +2068,17 @@ bool QScxmlParserPrivate::postReadElementScript()
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementSend()
+bool QScxmlCompilerPrivate::postReadElementSend()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementCancel()
+bool QScxmlCompilerPrivate::postReadElementCancel()
{
return flushInstruction();
}
-bool QScxmlParserPrivate::postReadElementInvoke()
+bool QScxmlCompilerPrivate::postReadElementInvoke()
{
DocumentModel::Invoke *i = current().instruction->asInvoke();
const QString fileName = i->src;
@@ -2793,11 +2092,6 @@ bool QScxmlParserPrivate::postReadElementInvoke()
QXmlStreamReader reader(data);
parseSubDocument(i, &reader, fileName);
}
- } else { // invoke always expects sub document
- DocumentModel::ScxmlDocument *doc = new DocumentModel::ScxmlDocument(m_fileName);
- doc->root = new DocumentModel::Scxml(DocumentModel::XmlLocation(0, 0));
- i->content.reset(doc);
- m_doc->allSubDocuments.append(i->content.data());
}
} else if (!fileName.isEmpty()) {
addError(QStringLiteral("both src and content given to invoke"));
@@ -2806,23 +2100,19 @@ bool QScxmlParserPrivate::postReadElementInvoke()
return true;
}
-bool QScxmlParserPrivate::postReadElementFinalize()
+bool QScxmlCompilerPrivate::postReadElementFinalize()
{
return true;
}
-void QScxmlParserPrivate::resetDocument()
+void QScxmlCompilerPrivate::resetDocument()
{
m_doc.reset(new DocumentModel::ScxmlDocument(fileName()));
}
-bool QScxmlParserPrivate::readDocument()
+bool QScxmlCompilerPrivate::readDocument()
{
resetDocument();
- if (m_qtMode == QScxmlParser::QtModeEnabled)
- m_doc->qtMode = true;
- else if (m_qtMode == QScxmlParser::QtModeDisabled)
- m_doc->qtMode = false;
m_currentState = m_doc->root;
for (bool finished = false; !finished && !m_reader->hasError();) {
switch (m_reader->readNext()) {
@@ -2849,9 +2139,6 @@ bool QScxmlParserPrivate::readDocument()
case QXmlStreamReader::EndElement :
finished = true;
break;
- case QXmlStreamReader::Comment:
- parseComment();
- break;
default :
break;
}
@@ -2869,7 +2156,7 @@ bool QScxmlParserPrivate::readDocument()
return true;
}
-bool QScxmlParserPrivate::readElement()
+bool QScxmlCompilerPrivate::readElement()
{
const QStringRef currentTag = m_reader->name();
const QXmlStreamAttributes attributes = m_reader->attributes();
@@ -2964,9 +2251,6 @@ bool QScxmlParserPrivate::readElement()
if (current().collectChars())
current().chars.append(m_reader->text());
break;
- case QXmlStreamReader::Comment:
- parseComment();
- break;
default :
break;
}
@@ -3012,40 +2296,13 @@ bool QScxmlParserPrivate::readElement()
return true;
}
-void QScxmlParserPrivate::parseComment()
-{
- static const QString qtModeSwitch = QStringLiteral("enable-qt-mode:");
- const QStringRef commentText = m_reader->text();
- int qtModeIdx = commentText.indexOf(qtModeSwitch);
- if (qtModeIdx != -1) {
- qtModeIdx += qtModeSwitch.size();
- while (qtModeIdx < commentText.size()) {
- if (commentText.at(qtModeIdx).isSpace()) {
- ++qtModeIdx;
- } else {
- break;
- }
- }
- const QStringRef value = commentText.mid(qtModeIdx);
- if (value.startsWith(QStringLiteral("yes")) && isWordEnd(value, 3)) {
- if (m_qtMode == QScxmlParser::QtModeFromInputFile)
- m_doc->qtMode = true;
- } else if (value.startsWith(QStringLiteral("no")) && isWordEnd(value, 2)) {
- if (m_qtMode == QScxmlParser::QtModeFromInputFile)
- m_doc->qtMode = false;
- } else {
- addError(QStringLiteral("expected 'yes' or 'no' after enable-qt-mode in comment"));
- }
- }
-}
-
-void QScxmlParserPrivate::currentStateUp()
+void QScxmlCompilerPrivate::currentStateUp()
{
Q_ASSERT(m_currentState->parent);
m_currentState = m_currentState->parent;
}
-bool QScxmlParserPrivate::flushInstruction()
+bool QScxmlCompilerPrivate::flushInstruction()
{
if (!hasPrevious()) {
addError(QStringLiteral("missing instructionContainer"));
@@ -3061,48 +2318,45 @@ bool QScxmlParserPrivate::flushInstruction()
}
-QByteArray QScxmlParserPrivate::load(const QString &name, bool *ok) const
+QByteArray QScxmlCompilerPrivate::load(const QString &name, bool *ok)
{
- return m_loader->load(name, m_fileName.isEmpty() ?
- QString() : QFileInfo(m_fileName).path(), ok);
+ QStringList errs;
+ const QByteArray result = m_loader->load(name, m_fileName.isEmpty() ?
+ QString() : QFileInfo(m_fileName).path(), &errs);
+ for (const QString &err : errs)
+ addError(err);
+
+ *ok = errs.isEmpty();
+
+ return result;
}
-QVector<QScxmlError> QScxmlParserPrivate::errors() const
+QVector<QScxmlError> QScxmlCompilerPrivate::errors() const
{
return m_errors;
}
-void QScxmlParserPrivate::addError(const QString &msg)
+void QScxmlCompilerPrivate::addError(const QString &msg)
{
m_errors.append(QScxmlError(m_fileName, m_reader->lineNumber(), m_reader->columnNumber(), msg));
}
-void QScxmlParserPrivate::addError(const DocumentModel::XmlLocation &location, const QString &msg)
+void QScxmlCompilerPrivate::addError(const DocumentModel::XmlLocation &location, const QString &msg)
{
m_errors.append(QScxmlError(m_fileName, location.line, location.column, msg));
}
-QScxmlParser::QtMode QScxmlParserPrivate::qtMode() const
-{
- return m_qtMode;
-}
-
-void QScxmlParserPrivate::setQtMode(QScxmlParser::QtMode mode)
-{
- m_qtMode = mode;
-}
-
-DocumentModel::AbstractState *QScxmlParserPrivate::currentParent() const
+DocumentModel::AbstractState *QScxmlCompilerPrivate::currentParent() const
{
return m_currentState ? m_currentState->asAbstractState() : Q_NULLPTR;
}
-DocumentModel::XmlLocation QScxmlParserPrivate::xmlLocation() const
+DocumentModel::XmlLocation QScxmlCompilerPrivate::xmlLocation() const
{
return DocumentModel::XmlLocation(m_reader->lineNumber(), m_reader->columnNumber());
}
-bool QScxmlParserPrivate::maybeId(const QXmlStreamAttributes &attributes, QString *id)
+bool QScxmlCompilerPrivate::maybeId(const QXmlStreamAttributes &attributes, QString *id)
{
Q_ASSERT(id);
QString idStr = attributes.value(QLatin1String("id")).toString();
@@ -3117,7 +2371,7 @@ bool QScxmlParserPrivate::maybeId(const QXmlStreamAttributes &attributes, QStrin
return true;
}
-DocumentModel::If *QScxmlParserPrivate::lastIf()
+DocumentModel::If *QScxmlCompilerPrivate::lastIf()
{
if (!hasPrevious()) {
addError(QStringLiteral("No previous instruction found for else block"));
@@ -3137,35 +2391,35 @@ DocumentModel::If *QScxmlParserPrivate::lastIf()
return ifI;
}
-QScxmlParserPrivate::ParserState &QScxmlParserPrivate::current()
+QScxmlCompilerPrivate::ParserState &QScxmlCompilerPrivate::current()
{
return m_stack.last();
}
-QScxmlParserPrivate::ParserState &QScxmlParserPrivate::previous()
+QScxmlCompilerPrivate::ParserState &QScxmlCompilerPrivate::previous()
{
return m_stack[m_stack.count() - 2];
}
-bool QScxmlParserPrivate::hasPrevious() const
+bool QScxmlCompilerPrivate::hasPrevious() const
{
return m_stack.count() > 1;
}
-bool QScxmlParserPrivate::checkAttributes(const QXmlStreamAttributes &attributes,
- QScxmlParserPrivate::ParserState::Kind kind)
+bool QScxmlCompilerPrivate::checkAttributes(const QXmlStreamAttributes &attributes,
+ QScxmlCompilerPrivate::ParserState::Kind kind)
{
return checkAttributes(attributes,
ParserState::requiredAttributes(kind),
ParserState::optionalAttributes(kind));
}
-bool QScxmlParserPrivate::checkAttributes(const QXmlStreamAttributes &attributes,
+bool QScxmlCompilerPrivate::checkAttributes(const QXmlStreamAttributes &attributes,
const QStringList &requiredNames,
const QStringList &optionalNames)
{
QStringList required = requiredNames;
- foreach (const QXmlStreamAttribute &attribute, attributes) {
+ for (const QXmlStreamAttribute &attribute : attributes) {
const QStringRef ns = attribute.namespaceUri();
if (!ns.isEmpty() && ns != scxmlNamespace && ns != qtScxmlNamespace)
continue;
@@ -3184,15 +2438,14 @@ bool QScxmlParserPrivate::checkAttributes(const QXmlStreamAttributes &attributes
return true;
}
-QScxmlParserPrivate::DefaultLoader::DefaultLoader(QScxmlParser *parser)
- : Loader(parser)
+QScxmlCompilerPrivate::DefaultLoader::DefaultLoader()
+ : Loader()
{}
-QByteArray QScxmlParserPrivate::DefaultLoader::load(const QString &name, const QString &baseDir, bool *ok)
+QByteArray QScxmlCompilerPrivate::DefaultLoader::load(const QString &name, const QString &baseDir, QStringList *errors)
{
- Q_ASSERT(ok != nullptr);
-
- *ok = false;
+ QStringList errs;
+ QByteArray contents;
#ifdef BUILD_QSCXMLC
QString cleanName = name;
if (name.startsWith(QStringLiteral("file:")))
@@ -3201,28 +2454,29 @@ QByteArray QScxmlParserPrivate::DefaultLoader::load(const QString &name, const Q
#else
const QUrl url(name);
if (!url.isLocalFile() && !url.isRelative())
- parser()->addError(QStringLiteral("src attribute is not a local file (%1)").arg(name));
+ errs << QStringLiteral("src attribute is not a local file (%1)").arg(name);
QFileInfo fInfo = url.isLocalFile() ? url.toLocalFile() : name;
#endif // BUILD_QSCXMLC
if (fInfo.isRelative())
fInfo = QFileInfo(QDir(baseDir).filePath(fInfo.filePath()));
if (!fInfo.exists()) {
- parser()->addError(QStringLiteral("src attribute resolves to non existing file (%1)").arg(fInfo.filePath()));
+ errs << QStringLiteral("src attribute resolves to non existing file (%1)").arg(fInfo.filePath());
} else {
QFile f(fInfo.filePath());
- if (f.open(QFile::ReadOnly)) {
- *ok = true;
- return f.readAll();
- } else {
- parser()->addError(QStringLiteral("Failure opening file %1: %2")
- .arg(fInfo.filePath(), f.errorString()));
- }
+ if (f.open(QFile::ReadOnly))
+ contents = f.readAll();
+ else
+ errs << QStringLiteral("Failure opening file %1: %2")
+ .arg(fInfo.filePath(), f.errorString());
}
- return QByteArray();
+ if (errors)
+ *errors = errs;
+
+ return contents;
}
-QScxmlParserPrivate::ParserState::ParserState(QScxmlParserPrivate::ParserState::Kind someKind)
+QScxmlCompilerPrivate::ParserState::ParserState(QScxmlCompilerPrivate::ParserState::Kind someKind)
: kind(someKind)
, instruction(0)
, instructionContainer(0)
diff --git a/src/scxml/qscxmlparser.h b/src/scxml/qscxmlcompiler.h
index 3ce749f..411dfde 100644
--- a/src/scxml/qscxmlparser.h
+++ b/src/scxml/qscxmlcompiler.h
@@ -37,45 +37,33 @@
**
****************************************************************************/
-#ifndef SCXMLPARSER_H
-#define SCXMLPARSER_H
+#ifndef QSCXMLCOMPILER_H
+#define QSCXMLCOMPILER_H
#include <QtScxml/qscxmlerror.h>
-
-#include <QStringList>
-#include <QString>
+#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
class QXmlStreamReader;
class QScxmlStateMachine;
-class QScxmlParserPrivate;
-class Q_SCXML_EXPORT QScxmlParser
+class QScxmlCompilerPrivate;
+class Q_SCXML_EXPORT QScxmlCompiler
{
public:
class Q_SCXML_EXPORT Loader
{
public:
- Loader(QScxmlParser *parser);
+ Loader();
virtual ~Loader();
- virtual QByteArray load(const QString &name, const QString &baseDir, bool *ok) = 0;
-
- protected:
- QScxmlParser *parser() const;
-
- private:
- QScxmlParser *m_parser;
- };
-
- enum QtMode {
- QtModeDisabled,
- QtModeEnabled,
- QtModeFromInputFile
+ virtual QByteArray load(const QString &name,
+ const QString &baseDir,
+ QStringList *errors) = 0;
};
public:
- QScxmlParser(QXmlStreamReader *xmlReader);
- ~QScxmlParser();
+ QScxmlCompiler(QXmlStreamReader *xmlReader);
+ ~QScxmlCompiler();
QString fileName() const;
void setFileName(const QString &fileName);
@@ -83,21 +71,14 @@ public:
Loader *loader() const;
void setLoader(Loader *newLoader);
- void parse();
- QScxmlStateMachine *instantiateStateMachine() const;
- void instantiateDataModel(QScxmlStateMachine *stateMachine) const;
-
+ QScxmlStateMachine *compile();
QVector<QScxmlError> errors() const;
- void addError(const QString &msg);
-
- QtMode qtMode() const;
- void setQtMode(QtMode mode);
private:
- friend class QScxmlParserPrivate;
- QScxmlParserPrivate *d;
+ friend class QScxmlCompilerPrivate;
+ QScxmlCompilerPrivate *d;
};
QT_END_NAMESPACE
-#endif // SCXMLPARSER_H
+#endif // QSCXMLCOMPILER_H
diff --git a/src/scxml/qscxmlparser_p.h b/src/scxml/qscxmlcompiler_p.h
index 3103605..6909c2c 100644
--- a/src/scxml/qscxmlparser_p.h
+++ b/src/scxml/qscxmlcompiler_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef SCXMLPARSER_P_H
-#define SCXMLPARSER_P_H
+#ifndef QSCXMLCOMPILER_P_H
+#define QSCXMLCOMPILER_P_H
//
// W A R N I N G
@@ -51,7 +51,7 @@
// We mean it.
//
-#include "qscxmlparser.h"
+#include "qscxmlcompiler.h"
#include <QDir>
#include <QFileInfo>
@@ -98,6 +98,7 @@ struct Node {
virtual Transition *asTransition() { return Q_NULLPTR; }
virtual HistoryState *asHistoryState() { return Q_NULLPTR; }
virtual Scxml *asScxml() { return Q_NULLPTR; }
+ AbstractState *asAbstractState();
private:
Q_DISABLE_COPY(Node)
@@ -158,6 +159,7 @@ struct Send: public Instruction
QStringList namelist;
QVector<Param *> params;
QString content;
+ QString contentexpr;
Send(const XmlLocation &xmlLocation): Instruction(xmlLocation) {}
Send *asSend() Q_DECL_OVERRIDE { return this; }
@@ -269,7 +271,6 @@ struct StateContainer
virtual AbstractState *asAbstractState() { return Q_NULLPTR; }
virtual State *asState() { return Q_NULLPTR; }
virtual Scxml *asScxml() { return Q_NULLPTR; }
- Node *asNode() { return dynamic_cast<Node *>(this); }
};
struct AbstractState: public StateContainer
@@ -281,7 +282,7 @@ struct AbstractState: public StateContainer
struct State: public AbstractState, public StateOrTransition
{
- enum Type { Normal, Parallel, Initial, Final };
+ enum Type { Normal, Parallel, Final };
QStringList initial;
QVector<DataElement *> dataElements;
@@ -292,12 +293,13 @@ struct State: public AbstractState, public StateOrTransition
QVector<Invoke *> invokes;
Type type;
- QVector<AbstractState *> initialStates; // filled during verification
+ Transition *initialTransition; // when not set, it is filled during verification
State(const XmlLocation &xmlLocation)
: StateOrTransition(xmlLocation)
, doneData(Q_NULLPTR)
, type(Normal)
+ , initialTransition(Q_NULLPTR)
{}
void add(StateOrTransition *s) Q_DECL_OVERRIDE
@@ -313,14 +315,14 @@ struct State: public AbstractState, public StateOrTransition
struct Transition: public StateOrTransition
{
- enum Type { External, Internal };
+ enum Type { Internal, External, Synthetic };
QStringList events;
QScopedPointer<QString> condition;
QStringList targets;
InstructionSequence instructionsOnTransition;
Type type;
- QVector<AbstractState *> targetStates; // filled during verification
+ QVector<AbstractState *> targetStates; // when not set, it is filled during verification
Transition(const XmlLocation &xmlLocation)
: StateOrTransition(xmlLocation)
@@ -379,7 +381,7 @@ struct Scxml: public StateContainer, public Node
QScopedPointer<Script> script;
InstructionSequence initialSetup;
- QVector<AbstractState *> initialStates; // filled during verification
+ Transition *initialTransition;
Scxml(const XmlLocation &xmlLocation)
: Node(xmlLocation)
@@ -407,13 +409,11 @@ struct ScxmlDocument
QVector<Node *> allNodes;
QVector<InstructionSequence *> allSequences;
QVector<ScxmlDocument *> allSubDocuments; // weak pointers
- bool qtMode;
bool isVerified;
ScxmlDocument(const QString &fileName)
: fileName(fileName)
, root(Q_NULLPTR)
- , qtMode(false)
, isVerified(false)
{}
@@ -449,7 +449,9 @@ struct ScxmlDocument
{
Transition *t = newNode<Transition>(xmlLocation);
allTransitions.append(t);
- parent->add(t);
+ if (parent != nullptr) {
+ parent->add(t);
+ }
return t;
}
@@ -505,7 +507,7 @@ public:
void visit(InstructionSequence *sequence)
{
Q_ASSERT(sequence);
- Q_FOREACH (Instruction *instruction, *sequence) {
+ for (Instruction *instruction : qAsConst(*sequence)) {
Q_ASSERT(instruction);
instruction->accept(this);
}
@@ -513,7 +515,7 @@ public:
void visit(const QVector<DataElement *> &dataElements)
{
- Q_FOREACH (DataElement *dataElement, dataElements) {
+ for (DataElement *dataElement : dataElements) {
Q_ASSERT(dataElement);
dataElement->accept(this);
}
@@ -521,7 +523,7 @@ public:
void visit(const QVector<StateOrTransition *> &children)
{
- Q_FOREACH (StateOrTransition *child, children) {
+ for (StateOrTransition *child : children) {
Q_ASSERT(child);
child->accept(this);
}
@@ -529,7 +531,7 @@ public:
void visit(const InstructionSequences &sequences)
{
- Q_FOREACH (InstructionSequence *sequence, sequences) {
+ for (InstructionSequence *sequence : sequences) {
Q_ASSERT(sequence);
visit(sequence);
}
@@ -537,25 +539,21 @@ public:
void visit(const QVector<Param *> &params)
{
- Q_FOREACH (Param *param, params) {
+ for (Param *param : params) {
Q_ASSERT(param);
param->accept(this);
}
}
};
-Q_SCXML_EXPORT bool isValidCppIdentifier(const QString &str);
-Q_SCXML_EXPORT bool isValidQPropertyName(const QString &str);
-Q_SCXML_EXPORT bool isEventToBeGenerated(const QString &event);
-
} // DocumentModel namespace
-class Q_SCXML_EXPORT QScxmlParserPrivate
+class Q_SCXML_EXPORT QScxmlCompilerPrivate
{
public:
- static QScxmlParserPrivate *get(QScxmlParser *parser);
+ static QScxmlCompilerPrivate *get(QScxmlCompiler *compiler);
- QScxmlParserPrivate(QScxmlParser *parser, QXmlStreamReader *reader);
+ QScxmlCompilerPrivate(QXmlStreamReader *reader);
bool verifyDocument();
DocumentModel::ScxmlDocument *scxmlDocument() const;
@@ -563,21 +561,24 @@ public:
QString fileName() const;
void setFileName(const QString &fileName);
- QScxmlParser::Loader *loader() const;
- void setLoader(QScxmlParser::Loader *loader);
+ QScxmlCompiler::Loader *loader() const;
+ void setLoader(QScxmlCompiler::Loader *loader);
bool readDocument();
- void parseSubDocument(DocumentModel::Invoke *parentInvoke, QXmlStreamReader *reader, const QString &fileName);
- bool parseSubElement(DocumentModel::Invoke *parentInvoke, QXmlStreamReader *reader, const QString &fileName);
- QByteArray load(const QString &name, bool *ok) const;
+ void parseSubDocument(DocumentModel::Invoke *parentInvoke,
+ QXmlStreamReader *reader,
+ const QString &fileName);
+ bool parseSubElement(DocumentModel::Invoke *parentInvoke,
+ QXmlStreamReader *reader,
+ const QString &fileName);
+ QByteArray load(const QString &name, bool *ok);
QVector<QScxmlError> errors() const;
void addError(const QString &msg);
void addError(const DocumentModel::XmlLocation &location, const QString &msg);
-
- QScxmlParser::QtMode qtMode() const;
- void setQtMode(QScxmlParser::QtMode mode);
+ QScxmlStateMachine *instantiateStateMachine() const;
+ void instantiateDataModel(QScxmlStateMachine *stateMachine) const;
private:
DocumentModel::AbstractState *currentParent() const;
@@ -645,7 +646,6 @@ private:
bool readElement();
void resetDocument();
- void parseComment();
void currentStateUp();
bool flushInstruction();
@@ -698,14 +698,18 @@ private:
static QStringList optionalAttributes(Kind kind);
};
- class DefaultLoader: public QScxmlParser::Loader
+public:
+ class DefaultLoader: public QScxmlCompiler::Loader
{
public:
- DefaultLoader(QScxmlParser *parser);
- QByteArray load(const QString &name, const QString &baseDir, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL;
+ DefaultLoader();
+ QByteArray load(const QString &name,
+ const QString &baseDir,
+ QStringList *errors) Q_DECL_OVERRIDE Q_DECL_FINAL;
};
- bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlParserPrivate::ParserState::Kind kind);
+private:
+ bool checkAttributes(const QXmlStreamAttributes &attributes, QScxmlCompilerPrivate::ParserState::Kind kind);
ParserState &current();
ParserState &previous();
bool hasPrevious() const;
@@ -717,14 +721,13 @@ private:
QScopedPointer<DocumentModel::ScxmlDocument> m_doc;
DocumentModel::StateContainer *m_currentState;
DefaultLoader m_defaultLoader;
- QScxmlParser::Loader *m_loader;
+ QScxmlCompiler::Loader *m_loader;
QXmlStreamReader *m_reader;
QVector<ParserState> m_stack;
QVector<QScxmlError> m_errors;
- QScxmlParser::QtMode m_qtMode;
};
QT_END_NAMESPACE
-#endif // SCXMLPARSER_P_H
+#endif // QSCXMLCOMPILER_P_H
diff --git a/src/scxml/qscxmlcppdatamodel.cpp b/src/scxml/qscxmlcppdatamodel.cpp
index 15be429..bc09d65 100644
--- a/src/scxml/qscxmlcppdatamodel.cpp
+++ b/src/scxml/qscxmlcppdatamodel.cpp
@@ -92,7 +92,7 @@ class TheDataModel: public QScxmlCppDataModel
<script>
media = eventData().value(QStringLiteral(&quot;media&quot;)).toString();
</script>
- <send type="qt:signal" event="playbackStarted">
+ <send event="playbackStarted">
<param name="media" expr="media"/>
</send>
</onentry>
@@ -239,7 +239,7 @@ bool QScxmlCppDataModel::setScxmlProperty(const QString &name, const QVariant &v
* Returns \c true if the state machine is in the state specified by \a stateName, \c false
* otherwise.
*/
-bool QScxmlCppDataModel::In(const QString &stateName) const
+bool QScxmlCppDataModel::inState(const QString &stateName) const
{
return stateMachine()->isActive(stateName);
}
diff --git a/src/scxml/qscxmlcppdatamodel.h b/src/scxml/qscxmlcppdatamodel.h
index 328dee8..fb59336 100644
--- a/src/scxml/qscxmlcppdatamodel.h
+++ b/src/scxml/qscxmlcppdatamodel.h
@@ -61,7 +61,7 @@ public:
explicit QScxmlCppDataModel(QObject *parent = nullptr);
~QScxmlCppDataModel();
- bool setup(const QVariantMap &initialDataValues) Q_DECL_OVERRIDE;
+ 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;
@@ -76,7 +76,7 @@ public:
bool hasScxmlProperty(const QString &name) const Q_DECL_OVERRIDE;
bool setScxmlProperty(const QString &name, const QVariant &value, const QString &context) Q_DECL_OVERRIDE;
- bool In(const QString &stateName) const;
+ bool inState(const QString &stateName) const;
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmldatamodel.h b/src/scxml/qscxmldatamodel.h
index d3ade56..845d8bf 100644
--- a/src/scxml/qscxmldatamodel.h
+++ b/src/scxml/qscxmldatamodel.h
@@ -74,7 +74,7 @@ public:
void setStateMachine(QScxmlStateMachine *stateMachine);
QScxmlStateMachine *stateMachine() const;
- virtual bool setup(const QVariantMap &initialDataValues) = 0;
+ Q_INVOKABLE virtual bool setup(const QVariantMap &initialDataValues) = 0;
#ifndef Q_QDOC
virtual QString evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) = 0;
diff --git a/src/scxml/qscxmldatamodel_p.h b/src/scxml/qscxmldatamodel_p.h
index 1fdcfbb..089071a 100644
--- a/src/scxml/qscxmldatamodel_p.h
+++ b/src/scxml/qscxmldatamodel_p.h
@@ -52,7 +52,7 @@
//
#include "qscxmldatamodel.h"
-#include "qscxmlparser_p.h"
+#include "qscxmlcompiler_p.h"
#include <private/qobject_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/scxml/qscxmlecmascriptdatamodel.cpp b/src/scxml/qscxmlecmascriptdatamodel.cpp
index ceba661..32c49f0 100644
--- a/src/scxml/qscxmlecmascriptdatamodel.cpp
+++ b/src/scxml/qscxmlecmascriptdatamodel.cpp
@@ -91,7 +91,7 @@ public:
QJSValue evalJSValue(const QString &expr, const QString &context, bool *ok)
{
- Q_ASSERT(engine());
+ assertEngine();
QString script = QStringLiteral("(function(){'use strict'; return (\n%1\n); })()").arg(expr);
return eval(script, context, ok);
@@ -100,11 +100,11 @@ public:
QJSValue eval(const QString &script, const QString &context, bool *ok)
{
Q_ASSERT(ok);
- Q_ASSERT(engine());
+ QJSEngine *engine = assertEngine();
// TODO: copy QJSEngine::evaluate and handle the case of v4->catchException() "our way"
- QJSValue v = engine()->evaluate(QStringLiteral("'use strict'; ") + script, QStringLiteral("<expr>"), 0);
+ QJSValue v = engine->evaluate(QStringLiteral("'use strict'; ") + script, QStringLiteral("<expr>"), 0);
if (v.isError()) {
*ok = false;
submitError(QStringLiteral("error.execution"),
@@ -118,8 +118,8 @@ public:
void setupDataModel()
{
- Q_ASSERT(engine());
- dataModel = engine()->globalObject();
+ QJSEngine *engine = assertEngine();
+ dataModel = engine->globalObject();
qCDebug(qscxmlLog) << stateMachine() << "initializing the datamodel";
setupSystemVariables();
@@ -132,17 +132,18 @@ public:
setReadonlyProperty(&dataModel, QStringLiteral("_name"), stateMachine()->name());
- auto scxml = engine()->newObject();
+ QJSEngine *engine = assertEngine();
+ auto scxml = engine->newObject();
scxml.setProperty(QStringLiteral("location"), QStringLiteral("#_scxml_%1").arg(stateMachine()->sessionId()));
- auto ioProcs = engine()->newObject();
+ 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, stateMachine());
dataModel.setProperty(QStringLiteral("_x"), platformVars->jsValue());
- dataModel.setProperty(QStringLiteral("In"),
- engine()->evaluate(QStringLiteral("function(id){return _x.In(id);}")));
+ dataModel.setProperty(QStringLiteral("In"), engine->evaluate(
+ QStringLiteral("function(id){return _x.inState(id);}")));
}
void assignEvent(const QScxmlEvent &event)
@@ -150,20 +151,21 @@ public:
if (event.name().isEmpty())
return;
- QJSValue _event = engine()->newObject();
+ QJSEngine *engine = assertEngine();
+ QJSValue _event = engine->newObject();
QJSValue dataValue = eventDataAsJSValue(event.data());
_event.setProperty(QStringLiteral("data"), dataValue.isUndefined() ? QJSValue(QJSValue::UndefinedValue)
: dataValue);
_event.setProperty(QStringLiteral("invokeid"), event.invokeId().isEmpty() ? QJSValue(QJSValue::UndefinedValue)
- : engine()->toScriptValue(event.invokeId()));
+ : engine->toScriptValue(event.invokeId()));
if (!event.originType().isEmpty())
- _event.setProperty(QStringLiteral("origintype"), engine()->toScriptValue(event.originType()));
+ _event.setProperty(QStringLiteral("origintype"), engine->toScriptValue(event.originType()));
_event.setProperty(QStringLiteral("origin"), event.origin().isEmpty() ? QJSValue(QJSValue::UndefinedValue)
- : engine()->toScriptValue(event.origin()) );
+ : engine->toScriptValue(event.origin()) );
_event.setProperty(QStringLiteral("sendid"), event.sendId().isEmpty() ? QJSValue(QJSValue::UndefinedValue)
- : engine()->toScriptValue(event.sendId()));
- _event.setProperty(QStringLiteral("type"), engine()->toScriptValue(event.scxmlType()));
- _event.setProperty(QStringLiteral("name"), engine()->toScriptValue(event.name()));
+ : engine->toScriptValue(event.sendId()));
+ _event.setProperty(QStringLiteral("type"), engine->toScriptValue(event.scxmlType()));
+ _event.setProperty(QStringLiteral("name"), engine->toScriptValue(event.name()));
_event.setProperty(QStringLiteral("raw"), QStringLiteral("unsupported")); // See test178
if (event.isErrorEvent())
_event.setProperty(QStringLiteral("errorMessage"), event.errorMessage());
@@ -171,18 +173,19 @@ public:
setReadonlyProperty(&dataModel, QStringLiteral("_event"), _event);
}
- QJSValue eventDataAsJSValue(const QVariant &eventData) const
+ QJSValue eventDataAsJSValue(const QVariant &eventData)
{
if (!eventData.isValid()) {
return QJSValue(QJSValue::UndefinedValue);
}
+ QJSEngine *engine = assertEngine();
if (eventData.canConvert<QVariantMap>()) {
auto keyValues = eventData.value<QVariantMap>();
- auto data = engine()->newObject();
+ auto data = engine->newObject();
for (QVariantMap::const_iterator it = keyValues.begin(), eit = keyValues.end(); it != eit; ++it) {
- data.setProperty(it.key(), engine()->toScriptValue(it.value()));
+ data.setProperty(it.key(), engine->toScriptValue(it.value()));
}
return data;
@@ -196,9 +199,9 @@ public:
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &err);
if (err.error == QJsonParseError::NoError)
- return engine()->toScriptValue(doc.toVariant());
+ return engine->toScriptValue(doc.toVariant());
else
- return engine()->toScriptValue(data);
+ return engine->toScriptValue(data);
}
QScxmlStateMachine *stateMachine() const
@@ -207,15 +210,21 @@ public:
return q->stateMachine();
}
- QJSEngine *engine() const
+ QJSEngine *assertEngine()
{
- if (jsEngine == Q_NULLPTR) {
- jsEngine = new QJSEngine(stateMachine());
+ if (!jsEngine) {
+ Q_Q(QScxmlEcmaScriptDataModel);
+ setEngine(new QJSEngine(q->stateMachine()));
}
return jsEngine;
}
+ QJSEngine *engine() const
+ {
+ return jsEngine;
+ }
+
void setEngine(QJSEngine *engine)
{ jsEngine = engine; }
@@ -225,9 +234,6 @@ public:
return q->tableData()->string(id);
}
- QScxmlStateMachine::BindingMethod dataBinding() const
- { return stateMachine()->dataBinding(); }
-
bool hasProperty(const QString &name) const
{ return dataModel.hasProperty(name); }
@@ -339,7 +345,7 @@ private: // Uses private API
}
private:
- mutable QJSEngine *jsEngine;
+ QJSEngine *jsEngine;
QJSValue dataModel;
};
@@ -386,7 +392,8 @@ bool QScxmlEcmaScriptDataModel::setup(const QVariantMap &initialDataValues)
QJSValue v = undefined;
QVariantMap::const_iterator it = initialDataValues.find(name);
if (it != initialDataValues.end()) {
- v = d->engine()->toScriptValue(it.value());
+ QJSEngine *engine = d->assertEngine();
+ v = engine->toScriptValue(it.value());
}
if (!d->setProperty(name, v, QStringLiteral("<data>"))) {
ok = false;
@@ -477,7 +484,9 @@ bool QScxmlEcmaScriptDataModel::evaluateForeach(EvaluatorId id, bool *ok, Foreac
}
QString item = d->string(info.item);
- if (engine()->evaluate(QStringLiteral("(function(){var %1 = 0})()").arg(item)).isError()) {
+
+ QJSEngine *engine = d->assertEngine();
+ if (engine->evaluate(QStringLiteral("(function(){var %1 = 0})()").arg(item)).isError()) {
d->submitError(QStringLiteral("error.execution"), QStringLiteral("invalid item '%1' in %2")
.arg(d->string(info.item), d->string(info.context)));
*ok = false;
@@ -541,27 +550,11 @@ bool QScxmlEcmaScriptDataModel::setScxmlProperty(const QString &name, const QVar
{
Q_D(QScxmlEcmaScriptDataModel);
Q_ASSERT(hasScxmlProperty(name));
- QJSValue v = d->engine()->toScriptValue(
+
+ QJSEngine *engine = d->assertEngine();
+ QJSValue v = engine->toScriptValue(
value.canConvert<QJSValue>() ? value.value<QJSValue>().toVariant() : value);
return d->setProperty(name, v, context);
}
-/*!
- * Returns the JavaScript engine used by this data model.
- */
-QJSEngine *QScxmlEcmaScriptDataModel::engine() const
-{
- Q_D(const QScxmlEcmaScriptDataModel);
- return d->engine();
-}
-
-/*!
- * Sets the JavaScript engine used by this data model to \a engine.
- */
-void QScxmlEcmaScriptDataModel::setEngine(QJSEngine *engine)
-{
- Q_D(QScxmlEcmaScriptDataModel);
- d->setEngine(engine);
-}
-
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlecmascriptdatamodel.h b/src/scxml/qscxmlecmascriptdatamodel.h
index 4c484f2..b1d9f55 100644
--- a/src/scxml/qscxmlecmascriptdatamodel.h
+++ b/src/scxml/qscxmlecmascriptdatamodel.h
@@ -54,7 +54,7 @@ public:
explicit QScxmlEcmaScriptDataModel(QObject *parent = nullptr);
~QScxmlEcmaScriptDataModel();
- bool setup(const QVariantMap &initialDataValues) Q_DECL_OVERRIDE;
+ 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;
@@ -71,9 +71,6 @@ public:
QVariant scxmlProperty(const QString &name) const Q_DECL_OVERRIDE;
bool hasScxmlProperty(const QString &name) const Q_DECL_OVERRIDE;
bool setScxmlProperty(const QString &name, const QVariant &value, const QString &context) Q_DECL_OVERRIDE;
-
- QJSEngine *engine() const;
- void setEngine(QJSEngine *engine);
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlecmascriptplatformproperties.cpp b/src/scxml/qscxmlecmascriptplatformproperties.cpp
index 8433d78..41e27b5 100644
--- a/src/scxml/qscxmlecmascriptplatformproperties.cpp
+++ b/src/scxml/qscxmlecmascriptplatformproperties.cpp
@@ -93,7 +93,7 @@ QString QScxmlPlatformProperties::marks() const
return QStringLiteral("the spot");
}
-bool QScxmlPlatformProperties::In(const QString &stateName)
+bool QScxmlPlatformProperties::inState(const QString &stateName)
{
return stateMachine()->isActive(stateName);
}
diff --git a/src/scxml/qscxmlecmascriptplatformproperties_p.h b/src/scxml/qscxmlecmascriptplatformproperties_p.h
index 75f9b1c..fef53ea 100644
--- a/src/scxml/qscxmlecmascriptplatformproperties_p.h
+++ b/src/scxml/qscxmlecmascriptplatformproperties_p.h
@@ -77,7 +77,7 @@ public:
QString marks() const;
- Q_INVOKABLE bool In(const QString &stateName);
+ Q_INVOKABLE bool inState(const QString &stateName);
private:
class Data;
diff --git a/src/scxml/qscxmlerror.cpp b/src/scxml/qscxmlerror.cpp
index 949ea9f..84cf4fe 100644
--- a/src/scxml/qscxmlerror.cpp
+++ b/src/scxml/qscxmlerror.cpp
@@ -62,10 +62,35 @@ public:
* \since 5.7
* \inmodule QtScxml
*
- * \sa QScxmlStateMachine QScxmlParser
+ * \sa QScxmlStateMachine QScxmlCompiler
*/
/*!
+ \property QScxmlError::column
+ \brief The column number in which the SCXML error occurred.
+*/
+
+/*!
+ \property QScxmlError::description
+ \brief A description of the SCXML error.
+*/
+
+/*!
+ \property QScxmlError::fileName
+ \brief The name of the file in which the SCXML error occurred.
+*/
+
+/*!
+ \property QScxmlError::line
+ \brief The line number on which the SCXML error occurred.
+*/
+
+/*!
+ \property QScxmlError::valid
+ \brief Whether the SCXML error is valid.
+*/
+
+/*!
* Creates a new invalid SCXML error.
*/
QScxmlError::QScxmlError()
diff --git a/src/scxml/qscxmlerror.h b/src/scxml/qscxmlerror.h
index 80325eb..cae249e 100644
--- a/src/scxml/qscxmlerror.h
+++ b/src/scxml/qscxmlerror.h
@@ -41,6 +41,7 @@
#define QSCXMLERROR_H
#include <QtScxml/qscxmlglobals.h>
+#include <QtCore/qobjectdefs.h>
#include <QString>
@@ -48,6 +49,15 @@ QT_BEGIN_NAMESPACE
class Q_SCXML_EXPORT QScxmlError
{
+#ifndef BUILD_QSCXMLC
+ Q_GADGET
+ Q_PROPERTY(bool valid READ isValid CONSTANT)
+ Q_PROPERTY(QString fileName READ fileName CONSTANT)
+ Q_PROPERTY(int line READ line CONSTANT)
+ Q_PROPERTY(int column READ column CONSTANT)
+ Q_PROPERTY(QString description READ description CONSTANT)
+#endif // BUILD_QSCXMLC
+
public:
QScxmlError();
QScxmlError(const QString &fileName, int line, int column, const QString &description);
diff --git a/src/scxml/qscxmlevent.cpp b/src/scxml/qscxmlevent.cpp
index bf5de7a..6da52d7 100644
--- a/src/scxml/qscxmlevent.cpp
+++ b/src/scxml/qscxmlevent.cpp
@@ -48,9 +48,6 @@ QT_BEGIN_NAMESPACE
using namespace QScxmlExecutableContent;
-QEvent::Type QScxmlEvent::scxmlEventType = (QEvent::Type) QEvent::registerEventType();
-QEvent::Type QScxmlEvent::ignoreEventType = (QEvent::Type) QEvent::registerEventType();
-
QAtomicInt QScxmlEventBuilder::idCounter = QAtomicInt(0);
QScxmlEvent *QScxmlEventBuilder::buildEvent()
@@ -140,7 +137,6 @@ QScxmlEvent *QScxmlEventBuilder::buildEvent()
return Q_NULLPTR;
}
if (!origintype.isEmpty()
- && origintype != QStringLiteral("qt:signal")
&& origintype != QStringLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor")) {
// [6.2.5] and test199
submitError(QStringLiteral("error.execution"),
@@ -180,7 +176,7 @@ QScxmlEvent *QScxmlEventBuilder::errorEvent(QScxmlStateMachine *stateMachine, co
return error;
}
-bool QScxmlEventBuilder::evaluate(const Param &param, QScxmlStateMachine *stateMachine,
+bool QScxmlEventBuilder::evaluate(const ParameterInfo &param, QScxmlStateMachine *stateMachine,
QVariantMap &keyValues)
{
auto dataModel = stateMachine->dataModel();
@@ -212,7 +208,8 @@ bool QScxmlEventBuilder::evaluate(const Param &param, QScxmlStateMachine *stateM
}
}
-bool QScxmlEventBuilder::evaluate(const QScxmlExecutableContent::Array<Param> *params, QScxmlStateMachine *stateMachine, QVariantMap &keyValues)
+bool QScxmlEventBuilder::evaluate(const QScxmlExecutableContent::Array<ParameterInfo> *params,
+ QScxmlStateMachine *stateMachine, QVariantMap &keyValues)
{
if (!params)
return true;
@@ -267,7 +264,7 @@ void QScxmlEventBuilder::submitError(const QString &type, const QString &msg, co
* Creates a new external SCXML event.
*/
QScxmlEvent::QScxmlEvent()
- : QEvent(scxmlEventType), d(new QScxmlEventPrivate)
+ : d(new QScxmlEventPrivate)
{ }
/*!
@@ -279,6 +276,12 @@ QScxmlEvent::~QScxmlEvent()
}
/*!
+ \property QScxmlEvent::scxmlType
+ \brief The event type.
+
+*/
+
+/*!
* Returns the event type.
*/
QString QScxmlEvent::scxmlType() const
@@ -308,7 +311,6 @@ void QScxmlEvent::clear()
*/
QScxmlEvent &QScxmlEvent::operator=(const QScxmlEvent &other)
{
- QEvent::operator=(other);
*d = *other.d;
return *this;
}
@@ -317,7 +319,7 @@ QScxmlEvent &QScxmlEvent::operator=(const QScxmlEvent &other)
* Constructs a copy of \a other.
*/
QScxmlEvent::QScxmlEvent(const QScxmlEvent &other)
- : QEvent(other), d(new QScxmlEventPrivate(*other.d))
+ : d(new QScxmlEventPrivate(*other.d))
{
}
@@ -460,6 +462,13 @@ void QScxmlEvent::setInvokeId(const QString &invokeid)
}
/*!
+ \property QScxmlEvent::delay
+
+ \brief The delay in milliseconds after which the event is to be delivered
+ after processing the \c <send> element.
+*/
+
+/*!
* Returns the delay in milliseconds after which this event is to be delivered
* after processing the \c <send> element.
*/
@@ -535,6 +544,11 @@ void QScxmlEvent::setData(const QVariant &data)
}
/*!
+ \property QScxmlEvent::errorEvent
+ \brief Whether the event represents an error.
+*/
+
+/*!
* Returns \c true when this is an error event, \c false otherwise.
*/
bool QScxmlEvent::isErrorEvent() const
@@ -543,6 +557,11 @@ bool QScxmlEvent::isErrorEvent() const
}
/*!
+ \property QScxmlEvent::errorMessage
+ \brief An error message for an error event, or an empty QString.
+*/
+
+/*!
* If this is an error event, returns the error message. Otherwise, returns an
* empty QString.
*/
@@ -562,11 +581,6 @@ void QScxmlEvent::setErrorMessage(const QString &message)
d->data = message;
}
-void QScxmlEvent::makeIgnorable()
-{
- t = ignoreEventType;
-}
-
QByteArray QScxmlEventPrivate::debugString(QScxmlEvent *event)
{
if (event == nullptr) {
diff --git a/src/scxml/qscxmlevent.h b/src/scxml/qscxmlevent.h
index 82efc57..c4f517f 100644
--- a/src/scxml/qscxmlevent.h
+++ b/src/scxml/qscxmlevent.h
@@ -42,28 +42,27 @@
#include <QtScxml/qscxmlglobals.h>
-#include <QEvent>
#include <QStringList>
#include <QVariantList>
QT_BEGIN_NAMESPACE
-namespace QScxmlInternal {
-class WrappedQStateMachine;
-}
-
class QScxmlEventPrivate;
-class Q_SCXML_EXPORT QScxmlEvent: public QEvent
+class Q_SCXML_EXPORT QScxmlEvent
{
Q_GADGET
- Q_PROPERTY(QString name READ name CONSTANT)
- Q_PROPERTY(QString eventType READ scxmlType CONSTANT)
- Q_PROPERTY(QString sendId READ sendId CONSTANT)
- Q_PROPERTY(QString origin READ origin CONSTANT)
- Q_PROPERTY(QString originType READ originType CONSTANT)
- Q_PROPERTY(QString invokeId READ invokeId CONSTANT)
- Q_PROPERTY(QVariant data READ data CONSTANT)
+ Q_PROPERTY(QString name READ name WRITE setName)
+ Q_PROPERTY(EventType eventType READ eventType WRITE setEventType)
+ Q_PROPERTY(QString scxmlType READ scxmlType)
+ Q_PROPERTY(QString sendId READ sendId WRITE setSendId)
+ Q_PROPERTY(QString origin READ origin WRITE setOrigin)
+ Q_PROPERTY(QString originType READ originType WRITE setOriginType)
+ Q_PROPERTY(QString invokeId READ invokeId WRITE setInvokeId)
+ Q_PROPERTY(int delay READ delay WRITE setDelay)
+ Q_PROPERTY(QVariant data READ data WRITE setData)
+ Q_PROPERTY(bool errorEvent READ isErrorEvent)
+ Q_PROPERTY(QString errorMessage READ errorMessage WRITE setErrorMessage)
public:
QScxmlEvent();
@@ -77,6 +76,7 @@ public:
InternalEvent,
ExternalEvent
};
+ Q_ENUM(EventType)
QString name() const;
void setName(const QString &name);
@@ -101,7 +101,7 @@ public:
int delay() const;
void setDelay(int delayInMiliSecs);
- void clear();
+ Q_INVOKABLE void clear();
QVariant data() const;
void setData(const QVariant &data);
@@ -110,14 +110,6 @@ public:
QString errorMessage() const;
void setErrorMessage(const QString &message);
-protected:
- friend class QScxmlInternal::WrappedQStateMachine;
-#ifndef Q_QDOC
- static QEvent::Type scxmlEventType;
- static QEvent::Type ignoreEventType;
- void makeIgnorable();
-#endif // Q_QDOC
-
private:
QScxmlEventPrivate *d;
diff --git a/src/scxml/qscxmlevent_p.h b/src/scxml/qscxmlevent_p.h
index d8d1361..aef14d1 100644
--- a/src/scxml/qscxmlevent_p.h
+++ b/src/scxml/qscxmlevent_p.h
@@ -71,7 +71,7 @@ class QScxmlEventBuilder
QScxmlExecutableContent::EvaluatorId eventexpr;
QString contents;
QScxmlExecutableContent::EvaluatorId contentExpr;
- const QScxmlExecutableContent::Array<QScxmlExecutableContent::Param> *params;
+ const QScxmlExecutableContent::Array<QScxmlExecutableContent::ParameterInfo> *params;
QScxmlEvent::EventType eventType;
QString id;
QString idLocation;
@@ -118,7 +118,7 @@ public:
eventType = QScxmlEvent::InternalEvent;
}
- QScxmlEventBuilder(QScxmlStateMachine *stateMachine, QScxmlExecutableContent::Send &send)
+ QScxmlEventBuilder(QScxmlStateMachine *stateMachine, const QScxmlExecutableContent::Send &send)
{
init();
this->stateMachine = stateMachine;
@@ -126,6 +126,7 @@ public:
event = stateMachine->tableData()->string(send.event);
eventexpr = send.eventexpr;
contents = stateMachine->tableData()->string(send.content);
+ contentExpr = send.contentexpr;
params = send.params();
id = stateMachine->tableData()->string(send.id);
idLocation = stateMachine->tableData()->string(send.idLocation);
@@ -143,12 +144,13 @@ public:
static QScxmlEvent *errorEvent(QScxmlStateMachine *stateMachine, const QString &name,
const QString &message, const QString &sendid);
- bool evaluate(const QScxmlExecutableContent::Param &param, QScxmlStateMachine *stateMachine,
- QVariantMap &keyValues);
-
- bool evaluate(const QScxmlExecutableContent::Array<QScxmlExecutableContent::Param> *params,
+ bool evaluate(const QScxmlExecutableContent::ParameterInfo &param,
QScxmlStateMachine *stateMachine, QVariantMap &keyValues);
+ bool evaluate(
+ const QScxmlExecutableContent::Array<QScxmlExecutableContent::ParameterInfo> *params,
+ QScxmlStateMachine *stateMachine, QVariantMap &keyValues);
+
void submitError(const QString &type, const QString &msg, const QString &sendid = QString());
};
#endif // BUILD_QSCXMLC
diff --git a/src/scxml/qscxmlexecutablecontent.cpp b/src/scxml/qscxmlexecutablecontent.cpp
index a11b4ca..5df8b1f 100644
--- a/src/scxml/qscxmlexecutablecontent.cpp
+++ b/src/scxml/qscxmlexecutablecontent.cpp
@@ -39,7 +39,7 @@
#include "qscxmlglobals_p.h"
#include "qscxmlexecutablecontent_p.h"
-#include "qscxmlparser_p.h"
+#include "qscxmlcompiler_p.h"
#include "qscxmlevent_p.h"
QT_BEGIN_NAMESPACE
@@ -96,64 +96,69 @@ bool QScxmlExecutionEngine::execute(ContainerId id, const QVariant &extraData)
if (id == NoInstruction)
return true;
- qint32 *ip = stateMachine->tableData()->instructions() + id;
+ const InstructionId *ip = stateMachine->tableData()->instructions() + id;
this->extraData = extraData;
- bool result = step(ip);
+ bool result = true;
+ step(ip, &result);
this->extraData = QVariant();
return result;
}
-bool QScxmlExecutionEngine::step(Instructions &ip)
+const InstructionId *QScxmlExecutionEngine::step(const InstructionId *ip, bool *ok)
{
auto dataModel = stateMachine->dataModel();
auto tableData = stateMachine->tableData();
- auto instr = reinterpret_cast<Instruction *>(ip);
+ *ok = true;
+ auto instr = reinterpret_cast<const Instruction *>(ip);
switch (instr->instructionType) {
case Instruction::Sequence: {
qCDebug(qscxmlLog) << stateMachine << "Executing sequence step";
- InstructionSequence *sequence = reinterpret_cast<InstructionSequence *>(instr);
+ const InstructionSequence *sequence = reinterpret_cast<const InstructionSequence *>(instr);
ip = sequence->instructions();
- Instructions end = ip + sequence->entryCount;
+ const InstructionId *end = ip + sequence->entryCount;
while (ip < end) {
- if (!step(ip)) {
- ip = end;
+ ip = step(ip, ok);
+ if (!(*ok)) {
qCDebug(qscxmlLog) << stateMachine << "Finished sequence step UNsuccessfully";
- return false;
+ return end;
}
}
qCDebug(qscxmlLog) << stateMachine << "Finished sequence step successfully";
- return true;
+ return ip;
}
case Instruction::Sequences: {
qCDebug(qscxmlLog) << stateMachine << "Executing sequences step";
- InstructionSequences *sequences = reinterpret_cast<InstructionSequences *>(instr);
+ const InstructionSequences *sequences
+ = reinterpret_cast<const InstructionSequences *>(instr);
ip += sequences->size();
for (int i = 0; i != sequences->sequenceCount; ++i) {
- Instructions sequence = sequences->at(i);
- step(sequence);
+ bool ignored;
+ const InstructionId *sequence = sequences->at(i);
+ step(sequence, &ignored);
}
qCDebug(qscxmlLog) << stateMachine << "Finished sequences step";
- return true;
+ return ip;
}
case Instruction::Send: {
qCDebug(qscxmlLog) << stateMachine << "Executing send step";
- Send *send = reinterpret_cast<Send *>(instr);
+ const Send *send = reinterpret_cast<const Send *>(instr);
ip += send->size();
QString delay = tableData->string(send->delay);
if (send->delayexpr != NoEvaluator) {
- bool ok = false;
- delay = stateMachine->dataModel()->evaluateToString(send->delayexpr, &ok);
- if (!ok)
- return false;
+ delay = stateMachine->dataModel()->evaluateToString(send->delayexpr, ok);
+ if (!(*ok))
+ return ip;
}
QScxmlEvent *event = QScxmlEventBuilder(stateMachine, *send).buildEvent();
- if (!event)
- return false;
+ if (!event) {
+ *ok = false;
+ return ip;
+ }
if (!delay.isEmpty()) {
int msecs = parseTime(delay);
@@ -161,94 +166,91 @@ bool QScxmlExecutionEngine::step(Instructions &ip)
event->setDelay(msecs);
} else {
qCDebug(qscxmlLog) << stateMachine << "failed to parse delay time" << delay;
- return false;
+ *ok = false;
+ return ip;
}
}
stateMachine->submitEvent(event);
- return true;
+ return ip;
}
case Instruction::JavaScript: {
qCDebug(qscxmlLog) << stateMachine << "Executing script step";
- JavaScript *javascript = reinterpret_cast<JavaScript *>(instr);
+ const JavaScript *javascript = reinterpret_cast<const JavaScript *>(instr);
ip += javascript->size();
- bool ok = true;
- dataModel->evaluateToVoid(javascript->go, &ok);
- return ok;
+ dataModel->evaluateToVoid(javascript->go, ok);
+ return ip;
}
case Instruction::If: {
qCDebug(qscxmlLog) << stateMachine << "Executing if step";
- If *_if = reinterpret_cast<If *>(instr);
+ const If *_if = reinterpret_cast<const If *>(instr);
ip += _if->size();
auto blocks = _if->blocks();
for (qint32 i = 0; i < _if->conditions.count; ++i) {
- bool ok = true;
- if (dataModel->evaluateToBool(_if->conditions.at(i), &ok) && ok) {
- Instructions block = blocks->at(i);
- bool res = step(block);
+ bool conditionOk = true;
+ if (dataModel->evaluateToBool(_if->conditions.at(i), &conditionOk) && conditionOk) {
+ const InstructionId *block = blocks->at(i);
+ step(block, ok);
qCDebug(qscxmlLog) << stateMachine << "Finished if step";
- return res;
+ return ip;
}
}
- if (_if->conditions.count < blocks->sequenceCount) {
- Instructions block = blocks->at(_if->conditions.count);
- return step(block);
- }
+ if (_if->conditions.count < blocks->sequenceCount)
+ step(blocks->at(_if->conditions.count), ok);
- return true;
+ return ip;
}
case Instruction::Foreach: {
class LoopBody: public QScxmlDataModel::ForeachLoopBody // If only we could put std::function in public API, we could use a lambda here. Alas....
{
QScxmlExecutionEngine *engine;
- const Instructions loopStart;
+ const InstructionId *loopStart;
public:
- LoopBody(QScxmlExecutionEngine *engine, const Instructions loopStart)
+ LoopBody(QScxmlExecutionEngine *engine, const InstructionId *loopStart)
: engine(engine)
, loopStart(loopStart)
{}
bool run() Q_DECL_OVERRIDE
{
- Instructions ip = loopStart;
- return engine->step(ip);
+ bool ok = true;
+ engine->step(loopStart, &ok);
+ return ok;
}
};
qCDebug(qscxmlLog) << stateMachine << "Executing foreach step";
- Foreach *foreach = reinterpret_cast<Foreach *>(instr);
- Instructions loopStart = foreach->blockstart();
- ip += foreach->size();
- bool ok = true;
+ const Foreach *_foreach = reinterpret_cast<const Foreach *>(instr);
+ const InstructionId *loopStart = _foreach->blockstart();
+ ip += _foreach->size();
LoopBody body(this, loopStart);
- bool evenMoreOk = dataModel->evaluateForeach(foreach->doIt, &ok, &body);
- return ok && evenMoreOk;
+ *ok = dataModel->evaluateForeach(_foreach->doIt, ok, &body) && *ok;
+ return ip;
}
case Instruction::Raise: {
qCDebug(qscxmlLog) << stateMachine << "Executing raise step";
- Raise *raise = reinterpret_cast<Raise *>(instr);
+ const Raise *raise = reinterpret_cast<const Raise *>(instr);
ip += raise->size();
auto name = tableData->string(raise->event);
auto event = new QScxmlEvent;
event->setName(name);
event->setEventType(QScxmlEvent::InternalEvent);
stateMachine->submitEvent(event);
- return true;
+ return ip;
}
case Instruction::Log: {
qCDebug(qscxmlLog) << stateMachine << "Executing log step";
- Log *log = reinterpret_cast<Log *>(instr);
+ const Log *log = reinterpret_cast<const Log *>(instr);
ip += log->size();
- bool ok = true;
- QString str = dataModel->evaluateToString(log->expr, &ok);
- if (ok) {
+ QString str = dataModel->evaluateToString(log->expr, ok);
+ if (*ok) {
const QString label = tableData->string(log->label);
qCDebug(scxmlLog) << label << ":" << str;
QMetaObject::invokeMethod(stateMachine,
@@ -257,411 +259,56 @@ bool QScxmlExecutionEngine::step(Instructions &ip)
Q_ARG(QString, label),
Q_ARG(QString, str));
}
- return ok;
+ return ip;
}
case Instruction::Cancel: {
qCDebug(qscxmlLog) << stateMachine << "Executing cancel step";
- Cancel *cancel = reinterpret_cast<Cancel *>(instr);
+ const Cancel *cancel = reinterpret_cast<const Cancel *>(instr);
ip += cancel->size();
QString e = tableData->string(cancel->sendid);
- bool ok = true;
if (cancel->sendidexpr != NoEvaluator)
- e = dataModel->evaluateToString(cancel->sendidexpr, &ok);
- if (ok && !e.isEmpty())
+ e = dataModel->evaluateToString(cancel->sendidexpr, ok);
+ if (*ok && !e.isEmpty())
stateMachine->cancelDelayedEvent(e);
- return ok;
+ return ip;
}
case Instruction::Assign: {
qCDebug(qscxmlLog) << stateMachine << "Executing assign step";
- Assign *assign = reinterpret_cast<Assign *>(instr);
+ const Assign *assign = reinterpret_cast<const Assign *>(instr);
ip += assign->size();
- bool ok = true;
- dataModel->evaluateAssignment(assign->expression, &ok);
- return ok;
+ dataModel->evaluateAssignment(assign->expression, ok);
+ return ip;
}
case Instruction::Initialize: {
qCDebug(qscxmlLog) << stateMachine << "Executing initialize step";
- Initialize *init = reinterpret_cast<Initialize *>(instr);
+ const Initialize *init = reinterpret_cast<const Initialize *>(instr);
ip += init->size();
- bool ok = true;
- dataModel->evaluateInitialization(init->expression, &ok);
- return ok;
+ dataModel->evaluateInitialization(init->expression, ok);
+ return ip;
}
case Instruction::DoneData: {
qCDebug(qscxmlLog) << stateMachine << "Executing DoneData step";
- DoneData *doneData = reinterpret_cast<DoneData *>(instr);
+ const DoneData *doneData = reinterpret_cast<const DoneData *>(instr);
QString eventName = QStringLiteral("done.state.") + extraData.toString();
QScxmlEventBuilder event(stateMachine, eventName, doneData);
+ auto e = event();
+ e->setEventType(QScxmlEvent::InternalEvent);
qCDebug(qscxmlLog) << stateMachine << "submitting event" << eventName;
- stateMachine->submitEvent(event());
- return true;
+ stateMachine->submitEvent(e);
+ return ip;
}
default:
Q_UNREACHABLE();
- return false;
+ *ok = false;
+ return ip;
}
}
#endif // BUILD_QSCXMLC
-Builder::Builder()
- : m_initialSetup(QScxmlExecutableContent::NoInstruction)
- , m_isCppDataModel(false)
-{
- m_activeSequences.reserve(4);
-}
-
-bool Builder::visit(DocumentModel::Send *node)
-{
- auto instr = m_instructions.add<Send>(Send::calculateExtraSize(node->params.size(), node->namelist.size()));
- instr->instructionLocation = createContext(QStringLiteral("send"));
- instr->event = addString(node->event);
- instr->eventexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("eventexpr"), node->eventexpr);
- instr->type = addString(node->type);
- instr->typeexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("typeexpr"), node->typeexpr);
- instr->target = addString(node->target);
- instr->targetexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("targetexpr"), node->targetexpr);
- instr->id = addString(node->id);
- instr->idLocation = addString(node->idLocation);
- instr->delay = addString(node->delay);
- instr->delayexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("delayexpr"), node->delayexpr);
- instr->content = addString(node->content);
- generate(&instr->namelist, node->namelist);
- generate(instr->params(), node->params);
- return false;
-}
-
-void Builder::visit(DocumentModel::Raise *node)
-{
- auto instr = m_instructions.add<Raise>();
- instr->event = addString(node->event);
-}
-
-void Builder::visit(DocumentModel::Log *node)
-{
- auto instr = m_instructions.add<Log>();
- instr->label = addString(node->label);
- instr->expr = createEvaluatorString(QStringLiteral("log"), QStringLiteral("expr"), node->expr);
-}
-
-void Builder::visit(DocumentModel::Script *node)
-{
- auto instr = m_instructions.add<JavaScript>();
- instr->go = createEvaluatorVoid(QStringLiteral("script"), QStringLiteral("source"), node->content);
-}
-
-void Builder::visit(DocumentModel::Assign *node)
-{
- auto instr = m_instructions.add<Assign>();
- auto ctxt = createContext(QStringLiteral("assign"), QStringLiteral("expr"), node->expr);
- instr->expression = addAssignment(node->location, node->expr, ctxt);
-}
-
-bool Builder::visit(DocumentModel::If *node)
-{
- auto instr = m_instructions.add<If>(node->conditions.size());
- instr->conditions.count = node->conditions.size();
- auto it = instr->conditions.data();
- QString tag = QStringLiteral("if");
- for (int i = 0, ei = node->conditions.size(); i != ei; ++i) {
- *it++ = createEvaluatorBool(tag, QStringLiteral("cond"), node->conditions.at(i));
- if (i == 0) {
- tag = QStringLiteral("elif");
- }
- }
- auto outSequences = m_instructions.add<InstructionSequences>();
- generate(outSequences, node->blocks);
- return false;
-}
-
-bool Builder::visit(DocumentModel::Foreach *node)
-{
- auto instr = m_instructions.add<Foreach>();
- auto ctxt = createContextString(QStringLiteral("foreach"));
- instr->doIt = addForeach(node->array, node->item, node->index, ctxt);
- startSequence(&instr->block);
- visit(&node->block);
- endSequence();
- return false;
-}
-
-void Builder::visit(DocumentModel::Cancel *node)
-{
- auto instr = m_instructions.add<Cancel>();
- instr->sendid = addString(node->sendid);
- instr->sendidexpr = createEvaluatorString(QStringLiteral("cancel"), QStringLiteral("sendidexpr"), node->sendidexpr);
-}
-
-ContainerId Builder::generate(const DocumentModel::DoneData *node)
-{
- auto id = m_instructions.newContainerId();
- DoneData *doneData;
- if (node) {
- doneData = m_instructions.add<DoneData>(node->params.size() * Param::calculateSize());
- doneData->contents = addString(node->contents);
- doneData->expr = createEvaluatorString(QStringLiteral("donedata"), QStringLiteral("expr"), node->expr);
- generate(&doneData->params, node->params);
- } else {
- doneData = m_instructions.add<DoneData>();
- doneData->contents = NoString;
- doneData->expr = NoEvaluator;
- doneData->params.count = 0;
- }
- doneData->location = createContext(QStringLiteral("final"));
- return id;
-}
-
-StringId Builder::createContext(const QString &instrName)
-{
- return addString(createContextString(instrName));
-}
-
-void Builder::generate(const QVector<DocumentModel::DataElement *> &dataElements)
-{
- foreach (DocumentModel::DataElement *el, dataElements) {
- auto ctxt = createContext(QStringLiteral("data"), QStringLiteral("expr"), el->expr);
- auto evaluator = addDataElement(el->id, el->expr, ctxt);
- if (evaluator != NoEvaluator) {
- auto instr = m_instructions.add<QScxmlExecutableContent::Initialize>();
- instr->expression = evaluator;
- }
- }
-}
-
-ContainerId Builder::generate(const DocumentModel::InstructionSequences &inSequences)
-{
- if (inSequences.isEmpty())
- return NoInstruction;
-
- auto id = m_instructions.newContainerId();
- auto outSequences = m_instructions.add<InstructionSequences>();
- generate(outSequences, inSequences);
- return id;
-}
-
-void Builder::generate(Array<Param> *out, const QVector<DocumentModel::Param *> &in)
-{
- out->count = in.size();
- Param *it = out->data();
- foreach (DocumentModel::Param *f, in) {
- it->name = addString(f->name);
- it->expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"), f->expr);
- it->location = addString(f->location);
- ++it;
- }
-}
-
-void Builder::generate(InstructionSequences *outSequences, const DocumentModel::InstructionSequences &inSequences)
-{
- int sequencesOffset = m_instructions.offset(outSequences);
- int sequenceCount = 0;
- int entryCount = 0;
- foreach (DocumentModel::InstructionSequence *sequence, inSequences) {
- ++sequenceCount;
- startNewSequence();
- visit(sequence);
- entryCount += endSequence()->size();
- }
- outSequences = m_instructions.at<InstructionSequences>(sequencesOffset);
- outSequences->sequenceCount = sequenceCount;
- outSequences->entryCount = entryCount;
-}
-
-void Builder::generate(Array<StringId> *out, const QStringList &in)
-{
- out->count = in.size();
- StringId *it = out->data();
- foreach (const QString &str, in) {
- *it++ = addString(str);
- }
-}
-
-ContainerId Builder::startNewSequence()
-{
- auto id = m_instructions.newContainerId();
- auto sequence = m_instructions.add<InstructionSequence>();
- startSequence(sequence);
- return id;
-}
-
-void Builder::startSequence(InstructionSequence *sequence)
-{
- SequenceInfo info;
- info.location = m_instructions.offset(sequence);
- info.entryCount = 0;
- m_activeSequences.push_back(info);
- m_instructions.setSequenceInfo(&m_activeSequences.last());
- sequence->instructionType = Instruction::Sequence;
- sequence->entryCount = -1; // checked in endSequence
-}
-
-InstructionSequence *Builder::endSequence()
-{
- SequenceInfo info = m_activeSequences.back();
- m_activeSequences.pop_back();
- m_instructions.setSequenceInfo(m_activeSequences.isEmpty() ? Q_NULLPTR : &m_activeSequences.last());
-
- auto sequence = m_instructions.at<InstructionSequence>(info.location);
- Q_ASSERT(sequence->entryCount == -1); // set in startSequence
- sequence->entryCount = info.entryCount;
- if (!m_activeSequences.isEmpty())
- m_activeSequences.last().entryCount += info.entryCount;
- return sequence;
-}
-
-EvaluatorId Builder::createEvaluatorString(const QString &instrName, const QString &attrName, const QString &expr)
-{
- if (!expr.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_stringEvaluators.insert(id, expr);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, expr);
- return addEvaluator(expr, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-EvaluatorId Builder::createEvaluatorBool(const QString &instrName, const QString &attrName, const QString &cond)
-{
- if (!cond.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_boolEvaluators.insert(id, cond);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, cond);
- return addEvaluator(cond, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-EvaluatorId Builder::createEvaluatorVariant(const QString &instrName, const QString &attrName, const QString &expr)
-{
- if (!expr.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_variantEvaluators.insert(id, expr);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, expr);
- return addEvaluator(expr, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-EvaluatorId Builder::createEvaluatorVoid(const QString &instrName, const QString &attrName, const QString &stuff)
-{
- if (!stuff.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_voidEvaluators.insert(id, stuff);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, stuff);
- return addEvaluator(stuff, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-DynamicTableData *Builder::tableData()
-{
- auto td = new DynamicTableData;
- td->strings = m_stringTable.data();
- td->theInstructions = m_instructions.data();
- td->theEvaluators = m_evaluators.data();
- td->theAssignments = m_assignments.data();
- td->theForeaches = m_foreaches.data();
- td->theDataNameIds = m_dataIds;
- td->theInitialSetup = m_initialSetup;
- td->theName = m_name;
- return td;
-}
-
-QString DynamicTableData::string(StringId id) const
-{
- return id == NoString ? QString() : strings.at(id);
-}
-
-Instructions DynamicTableData::instructions() const
-{
- return const_cast<Instructions>(theInstructions.data());
-}
-
-EvaluatorInfo DynamicTableData::evaluatorInfo(EvaluatorId evaluatorId) const
-{
- return theEvaluators[evaluatorId];
-}
-
-AssignmentInfo DynamicTableData::assignmentInfo(EvaluatorId assignmentId) const
-{
- return theAssignments[assignmentId];
-}
-
-ForeachInfo DynamicTableData::foreachInfo(EvaluatorId foreachId) const
-{
- return theForeaches[foreachId];
-}
-
-StringId *DynamicTableData::dataNames(int *count) const
-{
- Q_ASSERT(count);
- *count = theDataNameIds.size();
- return const_cast<StringId *>(theDataNameIds.data());
-}
-
-ContainerId DynamicTableData::initialSetup() const
-{
- return theInitialSetup;
-}
-
-QString DynamicTableData::name() const
-{
- return theName;
-}
-
-QVector<qint32> DynamicTableData::instructionTable() const
-{
- return theInstructions;
-}
-
-QVector<QString> DynamicTableData::stringTable() const
-{
- return strings;
-}
-
-QVector<EvaluatorInfo> DynamicTableData::evaluators() const
-{
- return theEvaluators;
-}
-
-QVector<AssignmentInfo> DynamicTableData::assignments() const
-{
- return theAssignments;
-}
-
-QVector<ForeachInfo> DynamicTableData::foreaches() const
-{
- return theForeaches;
-}
-
-StringIds DynamicTableData::allDataNameIds() const
-{
- return theDataNameIds;
-}
-
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlexecutablecontent.h b/src/scxml/qscxmlexecutablecontent.h
index 4bcfc36..cdf6b2b 100644
--- a/src/scxml/qscxmlexecutablecontent.h
+++ b/src/scxml/qscxmlexecutablecontent.h
@@ -42,20 +42,18 @@
#include <QtScxml/qscxmlglobals.h>
-#include <QVector>
-
QT_BEGIN_NAMESPACE
namespace QScxmlExecutableContent {
-typedef int ContainerId;
-enum { NoInstruction = -1 };
+typedef qint32 ContainerId;
+enum { NoContainer = -1 };
typedef qint32 StringId;
-typedef QVector<StringId> StringIds;
enum { NoString = -1 };
-typedef qint32 *Instructions;
-
-class QScxmlExecutionEngine;
+typedef qint32 InstructionId;
+enum { NoInstruction = -1 };
+typedef qint32 EvaluatorId;
+enum { NoEvaluator = -1 };
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(push, 4) // 4 == sizeof(qint32)
@@ -77,12 +75,26 @@ struct ForeachInfo {
StringId index;
StringId context;
};
+
+struct ParameterInfo {
+ StringId name;
+ EvaluatorId expr;
+ StringId location;
+};
+
+struct InvokeInfo {
+ StringId id;
+ StringId prefix;
+ StringId location;
+ StringId context;
+ EvaluatorId expr;
+ ContainerId finalize;
+ bool autoforward;
+};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(pop)
#endif
-typedef qint32 EvaluatorId;
-enum { NoEvaluator = -1 };
} // QScxmlExecutableContent namespace
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlexecutablecontent_p.h b/src/scxml/qscxmlexecutablecontent_p.h
index 0d2e286..4429686 100644
--- a/src/scxml/qscxmlexecutablecontent_p.h
+++ b/src/scxml/qscxmlexecutablecontent_p.h
@@ -52,10 +52,9 @@
//
#include <QtScxml/qscxmlexecutablecontent.h>
-#include <QtScxml/qscxmltabledata.h>
-#include <QtScxml/private/qscxmlparser_p.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qvariant.h>
+#include <QtScxml/private/qscxmltabledata_p.h>
+#include <QtScxml/private/qscxmlcompiler_p.h>
+#include <QTextStream>
#ifndef BUILD_QSCXMLC
#include <QtScxml/qscxmldatamodel.h>
@@ -104,20 +103,11 @@ struct Array
T *data() { return const_cast<T *>(const_data()); }
const T *const_data() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + sizeof(Array<T>)); }
- const T &at(int pos) { return *(data() + pos); }
+ const T &at(int pos) const { return *(const_data() + pos); }
int dataSize() const { return count * sizeof(T) / sizeof(qint32); }
int size() const { return sizeof(Array<T>) / sizeof(qint32) + dataSize(); }
};
-struct Q_SCXML_EXPORT Param
-{
- StringId name;
- EvaluatorId expr;
- StringId location;
-
- static int calculateSize() { return sizeof(Param) / sizeof(qint32); }
-};
-
struct Q_SCXML_EXPORT Instruction
{
enum InstructionType: qint32 {
@@ -141,7 +131,7 @@ struct Q_SCXML_EXPORT DoneData: Instruction
StringId location;
StringId contents;
EvaluatorId expr;
- Array<Param> params;
+ Array<ParameterInfo> params;
static InstructionType kind() { return Instruction::DoneData; }
};
@@ -152,7 +142,11 @@ struct Q_SCXML_EXPORT InstructionSequence: Instruction
// Instruction[] instructions;
static InstructionType kind() { return Instruction::Sequence; }
- Instructions instructions() { return reinterpret_cast<Instructions>(this) + sizeof(InstructionSequence) / sizeof(qint32); }
+ const InstructionId *instructions() const
+ {
+ return reinterpret_cast<const InstructionId *>(this)
+ + sizeof(InstructionSequence) / sizeof(qint32);
+ }
int size() const { return sizeof(InstructionSequence) / sizeof(qint32) + entryCount; }
};
@@ -163,14 +157,17 @@ struct Q_SCXML_EXPORT InstructionSequences: Instruction
// InstructionSequence[] sequences;
static InstructionType kind() { return Instruction::Sequences; }
- InstructionSequence *sequences() {
- return reinterpret_cast<InstructionSequence *>(reinterpret_cast<Instructions>(this) + sizeof(InstructionSequences) / sizeof(qint32));
+ const InstructionSequence *sequences() const {
+ return reinterpret_cast<const InstructionSequence *>(
+ reinterpret_cast<const InstructionId *>(this)
+ + sizeof(InstructionSequences) / sizeof(qint32));
}
int size() const { return sizeof(InstructionSequences)/sizeof(qint32) + entryCount; }
- Instructions at(int pos) {
- Instructions seq = reinterpret_cast<Instructions>(sequences());
+ const InstructionId *at(int pos) const
+ {
+ const InstructionId *seq = reinterpret_cast<const InstructionId *>(sequences());
while (pos--) {
- seq += reinterpret_cast<InstructionSequence *>(seq)->size();
+ seq += reinterpret_cast<const InstructionSequence *>(seq)->size();
}
return seq;
}
@@ -190,17 +187,35 @@ struct Q_SCXML_EXPORT Send: Instruction
StringId delay;
EvaluatorId delayexpr;
StringId content;
+ EvaluatorId contentexpr;
Array<StringId> namelist;
// Array<Param> params;
static InstructionType kind() { return Instruction::Send; }
- int size() { return sizeof(Send) / sizeof(qint32) + namelist.dataSize() + params()->size(); }
- Array<Param> *params() {
- return reinterpret_cast<Array<Param> *>(
- reinterpret_cast<Instructions>(this) + sizeof(Send) / sizeof(qint32) + namelist.dataSize());
+
+ int paramsOffset() const
+ {
+ return sizeof(Send) / sizeof(qint32) + namelist.dataSize();
}
+
+ int size() const
+ {
+ return paramsOffset() + params()->size();
+ }
+
+ const Array<ParameterInfo> *params() const {
+ return reinterpret_cast<const Array<ParameterInfo> *>(
+ reinterpret_cast<const InstructionId *>(this) + paramsOffset());
+ }
+
+ Array<ParameterInfo> *params() {
+ return reinterpret_cast<Array<ParameterInfo> *>(
+ reinterpret_cast<InstructionId *>(this) + paramsOffset());
+ }
+
static int calculateExtraSize(int paramCount, int nameCount) {
- return 1 + paramCount * sizeof(Param) / sizeof(qint32) + nameCount * sizeof(StringId) / sizeof(qint32);
+ return 1 + paramCount * sizeof(ParameterInfo) / sizeof(qint32)
+ + nameCount * sizeof(StringId) / sizeof(qint32);
}
};
@@ -249,13 +264,17 @@ struct Q_SCXML_EXPORT If: Instruction
{
Array<EvaluatorId> conditions;
// InstructionSequences blocks;
- InstructionSequences *blocks() {
- return reinterpret_cast<InstructionSequences *>(
- reinterpret_cast<Instructions>(this) + sizeof(If) / sizeof(qint32) + conditions.dataSize());
+ const InstructionSequences *blocks() const {
+ return reinterpret_cast<const InstructionSequences *>(
+ reinterpret_cast<const InstructionId *>(this) + sizeof(If) / sizeof(qint32)
+ + conditions.dataSize());
}
static InstructionType kind() { return Instruction::If; }
- int size() { return sizeof(If) / sizeof(qint32) + blocks()->size() + conditions.dataSize(); }
+ int size() const
+ {
+ return sizeof(If) / sizeof(qint32) + blocks()->size() + conditions.dataSize();
+ }
};
struct Q_SCXML_EXPORT Foreach: Instruction
@@ -265,7 +284,10 @@ struct Q_SCXML_EXPORT Foreach: Instruction
static InstructionType kind() { return Instruction::Foreach; }
int size() const { return sizeof(Foreach) / sizeof(qint32) + block.entryCount; }
- Instructions blockstart() { return reinterpret_cast<Instructions>(&block); }
+ const InstructionId *blockstart() const
+ {
+ return reinterpret_cast<const InstructionId *>(&block);
+ }
};
struct Q_SCXML_EXPORT Cancel: Instruction
@@ -277,244 +299,197 @@ struct Q_SCXML_EXPORT Cancel: Instruction
int size() const { return sizeof(Cancel) / sizeof(qint32); }
};
-#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
-#pragma pack(pop)
-#endif
-
-class Q_SCXML_EXPORT DynamicTableData:
-#ifndef BUILD_QSCXMLC
- public QObject,
-#endif // BUILD_QSCXMLC
- public QScxmlTableData
-{
-#ifndef BUILD_QSCXMLC
- Q_OBJECT
-#endif
-
-public:
- QString string(StringId id) const Q_DECL_OVERRIDE;
- Instructions instructions() const Q_DECL_OVERRIDE;
- EvaluatorInfo evaluatorInfo(EvaluatorId evaluatorId) const Q_DECL_OVERRIDE;
- AssignmentInfo assignmentInfo(EvaluatorId assignmentId) const Q_DECL_OVERRIDE;
- ForeachInfo foreachInfo(EvaluatorId foreachId) const Q_DECL_OVERRIDE;
- StringId *dataNames(int *count) const Q_DECL_OVERRIDE;
- ContainerId initialSetup() const Q_DECL_OVERRIDE;
- QString name() const Q_DECL_OVERRIDE;
-
- QVector<qint32> instructionTable() const;
- QVector<QString> stringTable() const;
- QVector<EvaluatorInfo> evaluators() const;
- QVector<AssignmentInfo> assignments() const;
- QVector<ForeachInfo> foreaches() const;
- StringIds allDataNameIds() const;
-
-private:
- friend class Builder;
- QVector<QString> strings;
- QVector<qint32> theInstructions;
- QVector<EvaluatorInfo> theEvaluators;
- QVector<AssignmentInfo> theAssignments;
- QVector<ForeachInfo> theForeaches;
- StringIds theDataNameIds;
- EvaluatorId theInitialSetup;
- QString theName;
-};
-
-class Q_SCXML_EXPORT Builder: public DocumentModel::NodeVisitor
-{
-public:
- Builder();
-
-protected: // visitor
- using NodeVisitor::visit;
-
- bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Raise *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Log *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Script *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Assign *node) Q_DECL_OVERRIDE;
- bool visit(DocumentModel::If *node) Q_DECL_OVERRIDE;
- bool visit(DocumentModel::Foreach *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Cancel *node) Q_DECL_OVERRIDE;
-
-protected:
- ContainerId generate(const DocumentModel::DoneData *node);
- StringId createContext(const QString &instrName);
- void generate(const QVector<DocumentModel::DataElement *> &dataElements);
- ContainerId generate(const DocumentModel::InstructionSequences &inSequences);
- void generate(Array<Param> *out, const QVector<DocumentModel::Param *> &in);
- void generate(InstructionSequences *outSequences, const DocumentModel::InstructionSequences &inSequences);
- void generate(Array<StringId> *out, const QStringList &in);
- ContainerId startNewSequence();
- void startSequence(InstructionSequence *sequence);
- InstructionSequence *endSequence();
- EvaluatorId createEvaluatorString(const QString &instrName, const QString &attrName, const QString &expr);
- EvaluatorId createEvaluatorBool(const QString &instrName, const QString &attrName, const QString &cond);
- EvaluatorId createEvaluatorVariant(const QString &instrName, const QString &attrName, const QString &expr);
- EvaluatorId createEvaluatorVoid(const QString &instrName, const QString &attrName, const QString &stuff);
-
- virtual QString createContextString(const QString &instrName) const = 0;
- virtual QString createContext(const QString &instrName, const QString &attrName, const QString &attrValue) const = 0;
-
- DynamicTableData *tableData();
-
- StringId addString(const QString &str)
- { return str.isEmpty() ? NoString : m_stringTable.add(str); }
-
- void setInitialSetup(ContainerId id)
- { m_initialSetup = id; }
+struct StateTable {
+ int version;
+ int name;
+ enum: int {
+ InvalidDataModel = -1,
+ NullDataModel = 0,
+ EcmaScriptDataModel = 1,
+ CppDataModel = 2
+ } dataModel;
+ int childStates; // offset into offsets
+ int initialTransition;
+ int initialSetup;
+ enum: int { InvalidBinding = -1, EarlyBinding = 0, LateBinding = 1 } binding;
+ int maxServiceId;
+ int stateOffset, stateCount;
+ int transitionOffset, transitionCount;
+ int arrayOffset, arraySize;
+
+ enum { terminator = 0xc0ff33 };
+ enum { InvalidIndex = -1 };
+
+ struct State {
+ int name;
+ int parent;
+ enum: int {
+ Invalid = -1,
+ Normal = 0,
+ Parallel = 1,
+ Final = 2,
+ ShallowHistory = 3,
+ DeepHistory = 4
+ } type;
+ int initialTransition;
+ int initInstructions;
+ int entryInstructions;
+ int exitInstructions;
+ int doneData;
+ int childStates; // offset into arrays
+ int transitions; // offset into arrays
+ int serviceFactoryIds; // offset into arrays
+
+ State()
+ : name(InvalidIndex)
+ , parent(InvalidIndex)
+ , type(Invalid)
+ , initialTransition(InvalidIndex)
+ , initInstructions(InvalidIndex)
+ , entryInstructions(InvalidIndex)
+ , exitInstructions(InvalidIndex)
+ , doneData(InvalidIndex)
+ , childStates(InvalidIndex)
+ , transitions(InvalidIndex)
+ , serviceFactoryIds(InvalidIndex)
+ {}
- void setName(const QString &name)
- { m_name = name; }
+ bool isAtomic() const
+ { return childStates == InvalidIndex; }
- bool isCppDataModel() const
- { return m_isCppDataModel; }
+ bool isCompound() const
+ { return type == Normal && childStates != InvalidIndex; }
- void setIsCppDataModel(bool onoff)
- { m_isCppDataModel = onoff; }
+ bool parentIsScxmlElement() const
+ { return parent == InvalidIndex; }
- QMap<EvaluatorId, QString> boolEvaluators() const
- { return m_boolEvaluators; }
+ bool isHistoryState() const
+ { return type == ShallowHistory || type == DeepHistory; }
- QMap<EvaluatorId, QString> stringEvaluators() const
- { return m_stringEvaluators; }
+ bool isParallel() const
+ { return type == Parallel; }
+ };
- QMap<EvaluatorId, QString> variantEvaluators() const
- { return m_variantEvaluators; }
+ struct Transition {
+ int events; // offset into offsets
+ int condition;
+ enum: int {
+ Invalid = -1,
+ Internal = 0,
+ External = 1,
+ Synthetic = 2
+ } type;
+ int source;
+ int targets; // offset into offsets
+ int transitionInstructions;
+
+ Transition()
+ : events(InvalidIndex)
+ , condition(InvalidIndex)
+ , type(Invalid)
+ , source(InvalidIndex)
+ , targets(InvalidIndex)
+ , transitionInstructions(InvalidIndex)
+ {}
+ };
- QMap<EvaluatorId, QString> voidEvaluators() const
- { return m_voidEvaluators; }
+ struct Array {
+ Array(const int *start): start(start) {}
+ int size() const { return *start; }
+ bool isValid() const { return start != nullptr; }
-private:
- template <typename T, typename U>
- class Table {
- QVector<T> elements;
- QMap<T, int> indexForElement;
-
- public:
- U add(const T &s, bool uniqueOnly = true) {
- int pos = uniqueOnly ? indexForElement.value(s, -1) : -1;
- if (pos == -1) {
- pos = elements.size();
- elements.append(s);
- indexForElement.insert(s, pos);
- }
- return pos;
+ int operator[](int idx) const {
+ Q_ASSERT(idx >= 0);
+ Q_ASSERT(idx < size());
+ return *(start + idx + 1);
}
- QVector<T> data() {
- elements.squeeze();
- return elements;
- }
- };
- Table<QString, StringId> m_stringTable;
-
- struct SequenceInfo {
- int location;
- qint32 entryCount; // the amount of qint32's that the instructions take up
- };
- QVector<SequenceInfo> m_activeSequences;
+ struct const_iterator: public std::iterator<std::forward_iterator_tag, int, ptrdiff_t,
+ const int *, const int &>
+ {
+ const_iterator(const Array &a, int pos): a(a), pos(pos) {}
- class InstructionStorage {
- public:
- InstructionStorage()
- : m_info(Q_NULLPTR)
- {}
+ const_iterator &operator++() {
+ if (pos < a.size()) ++pos;
+ return *this;
+ }
- ContainerId newContainerId() const { return m_instr.size(); }
+ bool operator==(const const_iterator &other) const
+ { return &other.a == &a && other.pos == pos; }
- template <typename T>
- T *add(int extra = 0)
- {
- int pos = m_instr.size();
- int size = sizeof(T) / sizeof(qint32) + extra;
- if (m_info)
- m_info->entryCount += size;
- m_instr.resize(pos + size);
- T *instr = at<T>(pos);
- Q_ASSERT(instr->instructionType == 0);
- instr->instructionType = T::kind();
- return instr;
- }
+ bool operator!=(const StateTable::Array::const_iterator &other)
+ { return !this->operator==(other); }
- int offset(Instruction *instr) const
- {
- return reinterpret_cast<qint32 *>(instr) - m_instr.data();
- }
+ int operator*() const {
+ if (pos < a.size())
+ return a[pos];
+ else
+ return -1;
+ }
- template <typename T>
- T *at(int offset)
- {
- return reinterpret_cast<T *>(&m_instr[offset]);
- }
+ private:
+ const Array &a;
+ int pos;
+ };
- QVector<qint32> data()
- {
- m_instr.squeeze();
- return m_instr;
- }
+ const_iterator begin() const
+ { return const_iterator(*this, 0); }
- void setSequenceInfo(SequenceInfo *info)
- {
- m_info = info;
- }
+ const_iterator end() const
+ { return const_iterator(*this, size()); }
private:
- QVector<qint32> m_instr;
- SequenceInfo *m_info;
+ const int *start;
};
- InstructionStorage m_instructions;
- EvaluatorId addEvaluator(const QString &expr, const QString &context)
+ StateTable()
+ : version(InvalidIndex)
+ , name(InvalidIndex)
+ , dataModel(InvalidDataModel)
+ , childStates(InvalidIndex)
+ , initialTransition(InvalidIndex)
+ , initialSetup(InvalidIndex)
+ , binding(InvalidBinding)
+ , maxServiceId(InvalidIndex)
+ , stateOffset(InvalidIndex), stateCount(InvalidIndex)
+ , transitionOffset(InvalidIndex), transitionCount(InvalidIndex)
+ , arrayOffset(InvalidIndex), arraySize(InvalidIndex)
+ {}
+
+ const State &state(int idx) const
{
- EvaluatorInfo ei;
- ei.expr = addString(expr);
- ei.context = addString(context);
- return m_evaluators.add(ei);
+ Q_ASSERT(idx >= 0);
+ Q_ASSERT(idx < stateCount);
+ return reinterpret_cast<const State *>(
+ reinterpret_cast<const int *>(this) + stateOffset)[idx];
}
- EvaluatorId addAssignment(const QString &dest, const QString &expr, const QString &context)
+ const Transition &transition(int idx) const
{
- AssignmentInfo ai;
- ai.dest = addString(dest);
- ai.expr = addString(expr);
- ai.context = addString(context);
- return m_assignments.add(ai);
+ Q_ASSERT(idx >= 0);
+ Q_ASSERT(idx < transitionCount);
+ return reinterpret_cast<const Transition *>(
+ reinterpret_cast<const int *>(this) + transitionOffset)[idx];
}
- EvaluatorId addForeach(const QString &array, const QString &item, const QString &index, const QString &context)
+ const Array array(int idx) const
{
- ForeachInfo fi;
- fi.array = addString(array);
- fi.item = addString(item);
- fi.index = addString(index);
- fi.context = addString(context);
- return m_foreaches.add(fi);
+ Q_ASSERT(idx < arraySize);
+ if (idx >= 0) {
+ const int *start = reinterpret_cast<const int *>(this) + arrayOffset + idx;
+ Q_ASSERT(*start + idx < arraySize);
+ return Array(start);
+ } else {
+ return Array(nullptr);
+ }
}
+};
- EvaluatorId addDataElement(const QString &id, const QString &expr, const QString &context)
- {
- auto str = addString(id);
- if (!m_dataIds.contains(str))
- m_dataIds.append(str);
- if (expr.isEmpty())
- return NoEvaluator;
-
- return addAssignment(id, expr, context);
- }
+#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
+#pragma pack(pop)
+#endif
- Table<EvaluatorInfo, EvaluatorId> m_evaluators;
- Table<AssignmentInfo, EvaluatorId> m_assignments;
- Table<ForeachInfo, EvaluatorId> m_foreaches;
- QMap<EvaluatorId, QString> m_boolEvaluators;
- QMap<EvaluatorId, QString> m_stringEvaluators;
- QMap<EvaluatorId, QString> m_variantEvaluators;
- QMap<EvaluatorId, QString> m_voidEvaluators;
- StringIds m_dataIds;
- ContainerId m_initialSetup;
- QString m_name;
- bool m_isCppDataModel;
-};
+} // QScxmlExecutableContent namespace
class QScxmlExecutionEngine
{
@@ -523,17 +498,16 @@ class QScxmlExecutionEngine
public:
QScxmlExecutionEngine(QScxmlStateMachine *stateMachine);
- bool execute(ContainerId ip, const QVariant &extraData = QVariant());
+ bool execute(QScxmlExecutableContent::ContainerId ip, const QVariant &extraData = QVariant());
private:
- bool step(Instructions &ip);
+ const QScxmlExecutableContent::InstructionId *step(
+ const QScxmlExecutableContent::InstructionId *ip, bool *ok);
QScxmlStateMachine *stateMachine;
QVariant extraData;
};
-} // QScxmlExecutableContent namespace
-
QT_END_NAMESPACE
#endif // EXECUTABLECONTENT_P_H
diff --git a/src/scxml/qscxmlglobals.h b/src/scxml/qscxmlglobals.h
index 9501ab6..d9bf7b5 100644
--- a/src/scxml/qscxmlglobals.h
+++ b/src/scxml/qscxmlglobals.h
@@ -39,7 +39,7 @@
#ifndef SCXMLGLOBALS_H
#define SCXMLGLOBALS_H
-#include <qglobal.h>
+#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
diff --git a/src/scxml/qscxmlinvokableservice.cpp b/src/scxml/qscxmlinvokableservice.cpp
index d473915..9af6be7 100644
--- a/src/scxml/qscxmlinvokableservice.cpp
+++ b/src/scxml/qscxmlinvokableservice.cpp
@@ -38,49 +38,41 @@
****************************************************************************/
#include "qscxmlglobals_p.h"
-#include "qscxmlinvokableservice.h"
+#include "qscxmlinvokableservice_p.h"
#include "qscxmlstatemachine_p.h"
QT_BEGIN_NAMESPACE
-class QScxmlInvokableService::Data
+QScxmlInvokableServicePrivate::QScxmlInvokableServicePrivate(QScxmlStateMachine *parentStateMachine)
+ : parentStateMachine(parentStateMachine)
{
-public:
- Data(QScxmlInvokableServiceFactory *service, QScxmlStateMachine *parent)
- : service(service)
- , parent(parent)
- {}
-
- QScxmlInvokableServiceFactory *service;
- QScxmlStateMachine *parent;
-};
-
-QScxmlInvokableService::QScxmlInvokableService(QScxmlInvokableServiceFactory *service,
- QScxmlStateMachine *parent)
- : d(new Data(service, parent))
-{
- qRegisterMetaType<QScxmlInvokableService *>();
+ static int metaType = qRegisterMetaType<QScxmlInvokableService *>();
+ Q_UNUSED(metaType);
}
-QScxmlInvokableService::~QScxmlInvokableService()
-{
- delete d;
-}
+QScxmlInvokableServiceFactoryPrivate::QScxmlInvokableServiceFactoryPrivate(
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ : invokeInfo(invokeInfo)
+ , names(namelist)
+ , parameters(parameters)
+{}
-bool QScxmlInvokableService::autoforward() const
+QScxmlInvokableService::QScxmlInvokableService(QScxmlStateMachine *parentStateMachine,
+ QObject *parent) :
+ QObject(*(new QScxmlInvokableServicePrivate(parentStateMachine)), parent)
{
- return d->service->autoforward();
}
QScxmlStateMachine *QScxmlInvokableService::parentStateMachine() const
{
- return d->parent;
+ Q_D(const QScxmlInvokableService);
+ return d->parentStateMachine;
}
-void QScxmlInvokableService::finalize()
+void QScxmlInvokableService::finalize(QScxmlExecutableContent::ContainerId finalize)
{
- QScxmlExecutableContent::ContainerId finalize = d->service->finalizeContent();
-
if (finalize != QScxmlExecutableContent::NoInstruction) {
auto psm = parentStateMachine();
qCDebug(qscxmlLog) << psm << "running finalize on event";
@@ -89,45 +81,16 @@ void QScxmlInvokableService::finalize()
}
}
-QScxmlInvokableServiceFactory *QScxmlInvokableService::service() const
+QScxmlInvokableService::QScxmlInvokableService(QScxmlInvokableServicePrivate &dd, QObject *parent) :
+ QObject(dd, parent)
{
- return d->service;
}
-class QScxmlInvokableServiceFactory::Data
-{
-public:
- Data(QScxmlExecutableContent::StringId invokeLocation, QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist, bool autoforward,
- const QVector<QScxmlInvokableServiceFactory::Param> &params,
- QScxmlExecutableContent::ContainerId finalize)
- : invokeLocation(invokeLocation)
- , id(id)
- , idPrefix(idPrefix)
- , idlocation(idlocation)
- , namelist(namelist)
- , params(params)
- , finalize(finalize)
- , autoforward(autoforward)
- {}
-
- QScxmlExecutableContent::StringId invokeLocation;
- QScxmlExecutableContent::StringId id;
- QScxmlExecutableContent::StringId idPrefix;
- QScxmlExecutableContent::StringId idlocation;
- QVector<QScxmlExecutableContent::StringId> namelist;
- QVector<QScxmlInvokableServiceFactory::Param> params;
- QScxmlExecutableContent::ContainerId finalize;
- bool autoforward;
-};
-
QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
- QScxmlExecutableContent::StringId invokeLocation, QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix, QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist, bool autoforward,
- const QVector<Param> &params, QScxmlExecutableContent::ContainerId finalize)
- : d(new Data(invokeLocation, id, idPrefix, idlocation, namelist, autoforward, params, finalize))
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ : d(new QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters))
{}
QScxmlInvokableServiceFactory::~QScxmlInvokableServiceFactory()
@@ -135,30 +98,70 @@ QScxmlInvokableServiceFactory::~QScxmlInvokableServiceFactory()
delete d;
}
-QString QScxmlInvokableServiceFactory::calculateId(QScxmlStateMachine *parent, bool *ok) const
+QScxmlExecutableContent::InvokeInfo QScxmlInvokableServiceFactory::invokeInfo() const
+{
+ return d->invokeInfo;
+}
+
+QVector<QScxmlExecutableContent::ParameterInfo> QScxmlInvokableServiceFactory::parameters() const
+{
+ return d->parameters;
+}
+
+QVector<QScxmlExecutableContent::StringId> QScxmlInvokableServiceFactory::names() const
+{
+ return d->names;
+}
+
+QString QScxmlInvokableServiceFactoryPrivate::calculateSrcexpr(QScxmlStateMachine *parent,
+ bool *ok) const
+{
+ Q_ASSERT(ok);
+ *ok = true;
+ auto dataModel = parent->dataModel();
+
+ if (invokeInfo.expr != QScxmlExecutableContent::NoEvaluator) {
+ *ok = false;
+ auto v = dataModel->evaluateToString(invokeInfo.expr, ok);
+ if (!*ok)
+ return QString();
+ return v;
+ }
+
+ return QString();
+}
+
+QString QScxmlInvokableServicePrivate::calculateId(
+ QScxmlStateMachine *parent, const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ bool *ok) const
{
Q_ASSERT(ok);
*ok = true;
auto stateMachine = parent->tableData();
- if (d->id != QScxmlExecutableContent::NoString) {
- return stateMachine->string(d->id);
+ if (invokeInfo.id != QScxmlExecutableContent::NoString) {
+ return stateMachine->string(invokeInfo.id);
}
- QString id = QScxmlStateMachine::generateSessionId(stateMachine->string(d->idPrefix));
+ const QString newId = QScxmlStateMachinePrivate::generateSessionId(
+ stateMachine->string(invokeInfo.prefix));
- if (d->idlocation != QScxmlExecutableContent::NoString) {
- auto idloc = stateMachine->string(d->idlocation);
- auto ctxt = stateMachine->string(d->invokeLocation);
- *ok = parent->dataModel()->setScxmlProperty(idloc, id, ctxt);
+ if (invokeInfo.location != QScxmlExecutableContent::NoString) {
+ auto idloc = stateMachine->string(invokeInfo.location);
+ auto ctxt = stateMachine->string(invokeInfo.context);
+ *ok = parent->dataModel()->setScxmlProperty(idloc, newId, ctxt);
if (!*ok)
return QString();
}
- return id;
+ return newId;
}
-QVariantMap QScxmlInvokableServiceFactory::calculateData(QScxmlStateMachine *parent, bool *ok) const
+QVariantMap QScxmlInvokableServicePrivate::calculateData(
+ QScxmlStateMachine *parent,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ bool *ok) const
{
Q_ASSERT(ok);
@@ -166,7 +169,7 @@ QVariantMap QScxmlInvokableServiceFactory::calculateData(QScxmlStateMachine *par
auto dataModel = parent->dataModel();
auto tableData = parent->tableData();
- foreach (const Param &param, d->params) {
+ for (const QScxmlExecutableContent::ParameterInfo &param : parameters) {
auto name = tableData->string(param.name);
if (param.expr != QScxmlExecutableContent::NoEvaluator) {
@@ -192,7 +195,7 @@ QVariantMap QScxmlInvokableServiceFactory::calculateData(QScxmlStateMachine *par
}
}
- foreach (QScxmlExecutableContent::StringId locid, d->namelist) {
+ for (QScxmlExecutableContent::StringId locid : names) {
QString loc;
if (locid != QScxmlExecutableContent::NoString) {
loc = tableData->string(locid);
@@ -214,91 +217,106 @@ QVariantMap QScxmlInvokableServiceFactory::calculateData(QScxmlStateMachine *par
return result;
}
-bool QScxmlInvokableServiceFactory::autoforward() const
-{
- return d->autoforward;
-}
-
-QScxmlExecutableContent::ContainerId QScxmlInvokableServiceFactory::finalizeContent() const
-{
- return d->finalize;
-}
+QScxmlScxmlServicePrivate::QScxmlScxmlServicePrivate(QScxmlStateMachine *stateMachine,
+ QScxmlStateMachine *parentStateMachine) :
+ QScxmlInvokableServicePrivate(parentStateMachine), stateMachine(stateMachine)
+{}
-QScxmlInvokableScxml::QScxmlInvokableScxml(QScxmlInvokableServiceFactory *service,
- QScxmlStateMachine *stateMachine,
- QScxmlStateMachine *parent)
- : QScxmlInvokableService(service, parent)
- , m_stateMachine(stateMachine)
+QScxmlScxmlServicePrivate::~QScxmlScxmlServicePrivate()
{
- QScxmlStateMachinePrivate::get(stateMachine)->m_parentStateMachine = parent;
+ delete stateMachine;
}
-QScxmlInvokableScxml::~QScxmlInvokableScxml()
+QScxmlScxmlService::QScxmlScxmlService(QScxmlStateMachine *stateMachine,
+ QScxmlStateMachine *parentStateMachine,
+ QObject *parent)
+ : QScxmlInvokableService(*(new QScxmlScxmlServicePrivate(stateMachine, parentStateMachine)),
+ parent)
{
- delete m_stateMachine;
+ QScxmlStateMachinePrivate::get(stateMachine)->m_parentStateMachine = parentStateMachine;
}
-bool QScxmlInvokableScxml::start()
+bool QScxmlScxmlService::start(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QVector<QScxmlExecutableContent::StringId> &names)
{
- qCDebug(qscxmlLog) << parentStateMachine() << "preparing to start" << m_stateMachine;
+ Q_D(QScxmlScxmlService);
+ qCDebug(qscxmlLog) << parentStateMachine() << "preparing to start" << d->stateMachine;
bool ok = false;
- auto id = service()->calculateId(parentStateMachine(), &ok);
+ auto id = d->calculateId(parentStateMachine(), invokeInfo, &ok);
if (!ok)
return false;
- auto data = service()->calculateData(parentStateMachine(), &ok);
+ auto data = d->calculateData(parentStateMachine(), parameters, names, &ok);
if (!ok)
return false;
- m_stateMachine->setSessionId(id);
- m_stateMachine->setInitialValues(data);
- if (m_stateMachine->init()) {
- qCDebug(qscxmlLog) << parentStateMachine() << "starting" << m_stateMachine;
- m_stateMachine->start();
+ QScxmlStateMachinePrivate::get(d->stateMachine)->m_sessionId = id;
+ d->stateMachine->setInitialValues(data);
+ if (d->stateMachine->init()) {
+ qCDebug(qscxmlLog) << parentStateMachine() << "starting" << d->stateMachine;
+ d->stateMachine->start();
return true;
}
- qCDebug(qscxmlLog) << parentStateMachine() << "failed to start" << m_stateMachine;
+ qCDebug(qscxmlLog) << parentStateMachine() << "failed to start" << d->stateMachine;
return false;
}
-QString QScxmlInvokableScxml::id() const
+QString QScxmlScxmlService::id() const
{
- return m_stateMachine->sessionId();
+ Q_D(const QScxmlScxmlService);
+ return d->stateMachine->sessionId();
}
-QString QScxmlInvokableScxml::name() const
+QString QScxmlScxmlService::name() const
{
- return m_stateMachine->name();
+ Q_D(const QScxmlScxmlService);
+ return d->stateMachine->name();
}
-void QScxmlInvokableScxml::postEvent(QScxmlEvent *event)
+void QScxmlScxmlService::postEvent(QScxmlEvent *event)
{
- QScxmlStateMachinePrivate::get(m_stateMachine)->postEvent(event);
+ Q_D(QScxmlScxmlService);
+ QScxmlStateMachinePrivate::get(d->stateMachine)->postEvent(event);
}
-QScxmlStateMachine *QScxmlInvokableScxml::stateMachine() const
+QScxmlStateMachine *QScxmlScxmlService::stateMachine() const
{
- return m_stateMachine;
+ Q_D(const QScxmlScxmlService);
+ return d->stateMachine;
}
-QScxmlInvokableScxmlServiceFactory::QScxmlInvokableScxmlServiceFactory(
- QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool doAutoforward,
- const QVector<QScxmlInvokableServiceFactory::Param> &params,
- QScxmlExecutableContent::ContainerId finalize)
- : QScxmlInvokableServiceFactory(invokeLocation, id, idPrefix, idlocation, namelist,
- doAutoforward, params, finalize)
+QScxmlScxmlServiceFactory::QScxmlScxmlServiceFactory(
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ : QScxmlInvokableServiceFactory(invokeInfo, names, parameters)
+{}
+
+QScxmlScxmlService *QScxmlScxmlServiceFactory::invokeStatic(QScxmlStateMachine *childStateMachine,
+ QScxmlStateMachine *parentStateMachine)
+{
+ QScxmlStateMachinePrivate::get(childStateMachine)->setIsInvoked(true);
+ return new QScxmlScxmlService(childStateMachine, parentStateMachine);
+}
+
+QScxmlDynamicScxmlServiceFactory::QScxmlDynamicScxmlServiceFactory(
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ : QScxmlScxmlServiceFactory(invokeInfo, names, parameters)
{}
-QScxmlInvokableService *QScxmlInvokableScxmlServiceFactory::finishInvoke(QScxmlStateMachine *child, QScxmlStateMachine *parent)
+QScxmlInvokableService *QScxmlDynamicScxmlServiceFactory::invoke(
+ QScxmlStateMachine *parentStateMachine)
{
- QScxmlStateMachinePrivate::get(child)->setIsInvoked(true);
- return new QScxmlInvokableScxml(this, child, parent);
+ bool ok = true;
+ auto srcexpr = d->calculateSrcexpr(parentStateMachine, &ok);
+ if (!ok)
+ return Q_NULLPTR;
+
+ return invokeDynamic(parentStateMachine, srcexpr);
}
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlinvokableservice.h b/src/scxml/qscxmlinvokableservice.h
index 21ba707..6efc07d 100644
--- a/src/scxml/qscxmlinvokableservice.h
+++ b/src/scxml/qscxmlinvokableservice.h
@@ -40,121 +40,122 @@
#ifndef QSCXMLINVOKABLESERVICE_H
#define QSCXMLINVOKABLESERVICE_H
-#include <QtScxml/qscxmldatamodel.h>
-
-#include <QString>
-#include <QVariantMap>
+#include "qscxmldatamodel.h"
+#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
class QScxmlEvent;
class QScxmlStateMachine;
-class QScxmlInvokableServiceFactory;
-class Q_SCXML_EXPORT QScxmlInvokableService
+class QScxmlInvokableServicePrivate;
+class Q_SCXML_EXPORT QScxmlInvokableService : public QObject
{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QScxmlInvokableService)
+ Q_PROPERTY(QScxmlStateMachine *parentStateMachine READ parentStateMachine CONSTANT)
+ Q_PROPERTY(QString id READ id CONSTANT)
+ Q_PROPERTY(QString name READ name CONSTANT)
+
public:
- QScxmlInvokableService(QScxmlInvokableServiceFactory *service, QScxmlStateMachine *parentStateMachine);
- virtual ~QScxmlInvokableService();
+ QScxmlInvokableService(QScxmlStateMachine *parentStateMachine,
+ QObject *parent = nullptr);
- bool autoforward() const;
QScxmlStateMachine *parentStateMachine() const;
- virtual bool start() = 0;
+ virtual bool start(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QVector<QScxmlExecutableContent::StringId> &names) = 0;
virtual QString id() const = 0;
virtual QString name() const = 0;
virtual void postEvent(QScxmlEvent *event) = 0;
- void finalize();
+ void finalize(QScxmlExecutableContent::ContainerId finalize);
protected:
- QScxmlInvokableServiceFactory *service() const;
-
-private:
- class Data;
- Data *d;
+ QScxmlInvokableService(QScxmlInvokableServicePrivate &dd, QObject *parent = nullptr);
};
+class QScxmlInvokableServiceFactoryPrivate;
class Q_SCXML_EXPORT QScxmlInvokableServiceFactory
{
public:
- struct Q_SCXML_EXPORT Param
- {
- QScxmlExecutableContent::StringId name;
- QScxmlExecutableContent::EvaluatorId expr;
- QScxmlExecutableContent::StringId location;
-
- Param(QScxmlExecutableContent::StringId theName,
- QScxmlExecutableContent::EvaluatorId theExpr,
- QScxmlExecutableContent::StringId theLocation)
- : name(theName)
- , expr(theExpr)
- , location(theLocation)
- {}
-
- Param()
- : name(QScxmlExecutableContent::NoString)
- , expr(QScxmlExecutableContent::NoInstruction)
- , location(QScxmlExecutableContent::NoString)
- {}
- };
-
- QScxmlInvokableServiceFactory(QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool autoforward,
- const QVector<Param> &params,
- QScxmlExecutableContent::ContainerId finalizeContent);
+ QScxmlInvokableServiceFactory(
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters);
virtual ~QScxmlInvokableServiceFactory();
- virtual QScxmlInvokableService *invoke(QScxmlStateMachine *parent) = 0;
-
-public: // callbacks from the service:
- QString calculateId(QScxmlStateMachine *parent, bool *ok) const;
- QVariantMap calculateData(QScxmlStateMachine *parent, bool *ok) const;
- bool autoforward() const;
- QScxmlExecutableContent::ContainerId finalizeContent() const;
+ virtual QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) = 0;
+ QScxmlExecutableContent::InvokeInfo invokeInfo() const;
+ QVector<QScxmlExecutableContent::ParameterInfo> parameters() const;
+ QVector<QScxmlExecutableContent::StringId> names() const;
-private:
- class Data;
- Data *d;
+protected:
+ QScxmlInvokableServiceFactoryPrivate *d;
};
-class Q_SCXML_EXPORT QScxmlInvokableScxml: public QScxmlInvokableService
+class QScxmlScxmlServicePrivate;
+class Q_SCXML_EXPORT QScxmlScxmlService: public QScxmlInvokableService
{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QScxmlScxmlService)
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine CONSTANT)
public:
- QScxmlInvokableScxml(QScxmlInvokableServiceFactory *service,
- QScxmlStateMachine *stateMachine,
- QScxmlStateMachine *parentStateMachine);
- virtual ~QScxmlInvokableScxml();
+ QScxmlScxmlService(QScxmlStateMachine *stateMachine,
+ QScxmlStateMachine *parentStateMachine,
+ QObject *parent = nullptr);
- bool start() Q_DECL_OVERRIDE;
+ bool start(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ 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;
-
-private:
- QScxmlStateMachine *m_stateMachine;
};
-class Q_SCXML_EXPORT QScxmlInvokableScxmlServiceFactory: public QScxmlInvokableServiceFactory
+class Q_SCXML_EXPORT QScxmlScxmlServiceFactory: public QScxmlInvokableServiceFactory
{
public:
- QScxmlInvokableScxmlServiceFactory(QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool doAutoforward,
- const QVector<Param> &params,
- QScxmlExecutableContent::ContainerId finalize);
+ QScxmlScxmlServiceFactory(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters);
protected:
- QScxmlInvokableService *finishInvoke(QScxmlStateMachine *child, QScxmlStateMachine *parent);
+ QScxmlScxmlService *invokeDynamic(QScxmlStateMachine *parentStateMachine,
+ const QString &sourceUrl);
+ QScxmlScxmlService *invokeStatic(QScxmlStateMachine *childStateMachine,
+ QScxmlStateMachine *parentStateMachine);
+};
+
+template<class T>
+class QScxmlStaticScxmlServiceFactory: public QScxmlScxmlServiceFactory
+{
+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);
+ }
+};
+
+class Q_SCXML_EXPORT QScxmlDynamicScxmlServiceFactory: public QScxmlScxmlServiceFactory
+{
+public:
+ QScxmlDynamicScxmlServiceFactory(
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters);
+
+ QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) Q_DECL_OVERRIDE;
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlqstates_p.h b/src/scxml/qscxmlinvokableservice_p.h
index 02b69b3..52bd6d0 100644
--- a/src/scxml/qscxmlqstates_p.h
+++ b/src/scxml/qscxmlinvokableservice_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef SCXMLQSTATE_P_H
-#define SCXMLQSTATE_P_H
+#ifndef QSCXMLINVOKABLESERVICE_P_H
+#define QSCXMLINVOKABLESERVICE_P_H
//
// W A R N I N G
@@ -51,64 +51,51 @@
// We mean it.
//
-#include <QtScxml/qscxmlqstates.h>
-#include <QtCore/private/qabstracttransition_p.h>
-#include <QtCore/private/qstate_p.h>
-#include <QtCore/private/qfinalstate_p.h>
+#include "qscxmlinvokableservice.h"
+#include <QtCore/private/qobject_p.h>
QT_BEGIN_NAMESPACE
-class QScxmlStatePrivate: public QStatePrivate
+class QScxmlInvokableServicePrivate : public QObjectPrivate
{
- Q_DECLARE_PUBLIC(QScxmlState)
-
public:
- static QScxmlStatePrivate *get(QScxmlState *s) { return s ? s->d_func() : nullptr; }
+ QScxmlInvokableServicePrivate(QScxmlStateMachine *parentStateMachine);
- QScxmlStatePrivate();
- ~QScxmlStatePrivate();
+ QString calculateId(QScxmlStateMachine *parent,
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo, bool *ok) const;
+ QVariantMap calculateData(QScxmlStateMachine *parent,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ bool *ok) const;
- QScxmlExecutableContent::ContainerId initInstructions;
- QScxmlExecutableContent::ContainerId onEntryInstructions;
- QScxmlExecutableContent::ContainerId onExitInstructions;
- QVector<QScxmlInvokableServiceFactory *> invokableServiceFactories;
- QVector<QScxmlInvokableService *> invokedServices;
- QVector<QScxmlInvokableService *> servicesWaitingToStart;
+ QScxmlStateMachine *parentStateMachine;
};
-class QScxmlFinalStatePrivate: public QFinalStatePrivate
+class QScxmlInvokableServiceFactoryPrivate
{
- Q_DECLARE_PUBLIC(QScxmlFinalState)
-
public:
- static QScxmlFinalStatePrivate *get(QScxmlFinalState *s) { return s ? s->d_func() : nullptr; }
+ QScxmlInvokableServiceFactoryPrivate(
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters);
- QScxmlFinalStatePrivate();
- ~QScxmlFinalStatePrivate();
-
- QScxmlExecutableContent::ContainerId doneData;
- QScxmlExecutableContent::ContainerId onEntryInstructions;
- QScxmlExecutableContent::ContainerId onExitInstructions;
-};
+ QString calculateSrcexpr(QScxmlStateMachine *parent, bool *ok) const;
-class QScxmlBaseTransitionPrivate: public QAbstractTransitionPrivate
-{
- Q_DECLARE_PUBLIC(QScxmlBaseTransition)
-
-public:
- QStringList eventSelector;
+ QScxmlExecutableContent::InvokeInfo invokeInfo;
+ QVector<QScxmlExecutableContent::StringId> names;
+ QVector<QScxmlExecutableContent::ParameterInfo> parameters;
};
-class QScxmlTransitionPrivate: public QScxmlBaseTransitionPrivate
+class QScxmlScxmlServicePrivate : public QScxmlInvokableServicePrivate
{
public:
- QScxmlTransitionPrivate();
- ~QScxmlTransitionPrivate();
+ QScxmlScxmlServicePrivate(QScxmlStateMachine *stateMachine,
+ QScxmlStateMachine *parentStateMachine);
+ ~QScxmlScxmlServicePrivate();
- QScxmlExecutableContent::EvaluatorId conditionalExp;
- QScxmlExecutableContent::ContainerId instructionsOnTransition;
+ QScxmlStateMachine *stateMachine;
};
QT_END_NAMESPACE
-#endif // SCXMLQSTATE_P_H
+#endif // QSCXMLINVOKABLESERVICE_P_H
diff --git a/src/scxml/qscxmlnulldatamodel.h b/src/scxml/qscxmlnulldatamodel.h
index ee6f4ea..e4f7942 100644
--- a/src/scxml/qscxmlnulldatamodel.h
+++ b/src/scxml/qscxmlnulldatamodel.h
@@ -53,7 +53,7 @@ public:
explicit QScxmlNullDataModel(QObject *parent = nullptr);
~QScxmlNullDataModel();
- bool setup(const QVariantMap &initialDataValues) Q_DECL_OVERRIDE;
+ 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;
diff --git a/src/scxml/qscxmlqstates.cpp b/src/scxml/qscxmlqstates.cpp
deleted file mode 100644
index d40ef20..0000000
--- a/src/scxml/qscxmlqstates.cpp
+++ /dev/null
@@ -1,410 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qscxmlglobals_p.h"
-#include "qscxmlqstates_p.h"
-#include "qscxmlstatemachine_p.h"
-
-#undef DUMP_EVENT
-#ifdef DUMP_EVENT
-#include <QJSEngine>
-#include "qscxmlecmascriptdatamodel.h"
-#endif
-
-QT_BEGIN_NAMESPACE
-
-static QStringList filterEmpty(const QStringList &events) {
- QStringList res;
- int oldI = 0;
- for (int i = 0; i < events.size(); ++i) {
- if (events.at(i).isEmpty()) {
- res.append(events.mid(oldI, i - oldI));
- oldI = i + 1;
- }
- }
- if (oldI > 0) {
- res.append(events.mid(oldI));
- return res;
- }
- return events;
-}
-
-QScxmlStatePrivate::QScxmlStatePrivate()
- : initInstructions(QScxmlExecutableContent::NoInstruction)
- , onEntryInstructions(QScxmlExecutableContent::NoInstruction)
- , onExitInstructions(QScxmlExecutableContent::NoInstruction)
-{}
-
-QScxmlStatePrivate::~QScxmlStatePrivate()
-{
- qDeleteAll(invokableServiceFactories);
-}
-
-QScxmlState::QScxmlState(QState *parent)
- : QState(*new QScxmlStatePrivate, parent)
-{}
-
-QScxmlState::QScxmlState(QScxmlStateMachine *parent)
- : QState(*new QScxmlStatePrivate, QScxmlStateMachinePrivate::get(parent)->m_qStateMachine)
-{}
-
-QScxmlState::~QScxmlState()
-{}
-
-void QScxmlState::setAsInitialStateFor(QScxmlState *state)
-{
- state->setInitialState(this);
-}
-
-void QScxmlState::setAsInitialStateFor(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->setInitialState(this);
-}
-
-QScxmlStateMachine *QScxmlState::stateMachine() const {
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-QString QScxmlState::stateLocation() const
-{
- return QStringLiteral("State %1").arg(objectName());
-}
-
-void QScxmlState::setInitInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlState);
- d->initInstructions = instructions;
-}
-
-void QScxmlState::setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlState);
- d->onEntryInstructions = instructions;
-}
-
-void QScxmlState::setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlState);
- d->onExitInstructions = instructions;
-}
-
-void QScxmlState::setInvokableServiceFactories(const QVector<QScxmlInvokableServiceFactory *> &factories)
-{
- Q_D(QScxmlState);
- d->invokableServiceFactories = factories;
-}
-
-void QScxmlState::onEntry(QEvent *event)
-{
- Q_D(QScxmlState);
-
- auto sp = QScxmlStateMachinePrivate::get(stateMachine());
- if (d->initInstructions != QScxmlExecutableContent::NoInstruction) {
- sp->m_executionEngine->execute(d->initInstructions);
- d->initInstructions = QScxmlExecutableContent::NoInstruction;
- }
- QState::onEntry(event);
- auto sm = stateMachine();
- QScxmlStateMachinePrivate::get(sm)->m_executionEngine->execute(d->onEntryInstructions);
- foreach (QScxmlInvokableServiceFactory *f, d->invokableServiceFactories) {
- if (auto service = f->invoke(stateMachine())) {
- d->invokedServices.append(service);
- d->servicesWaitingToStart.append(service);
- sp->addService(service);
- }
- }
- emit didEnter();
-}
-
-void QScxmlState::onExit(QEvent *event)
-{
- Q_D(QScxmlState);
-
- emit willExit();
- auto sm = stateMachine();
- QScxmlStateMachinePrivate::get(sm)->m_executionEngine->execute(d->onExitInstructions);
- QState::onExit(event);
-}
-
-QScxmlFinalStatePrivate::QScxmlFinalStatePrivate()
- : doneData(QScxmlExecutableContent::NoInstruction)
- , onEntryInstructions(QScxmlExecutableContent::NoInstruction)
- , onExitInstructions(QScxmlExecutableContent::NoInstruction)
-{}
-
-QScxmlFinalStatePrivate::~QScxmlFinalStatePrivate()
-{}
-
-QScxmlFinalState::QScxmlFinalState(QState *parent)
- : QFinalState(*new QScxmlFinalStatePrivate, parent)
-{}
-
-QScxmlFinalState::QScxmlFinalState(QScxmlStateMachine *parent)
- : QFinalState(*new QScxmlFinalStatePrivate, QScxmlStateMachinePrivate::get(parent)->m_qStateMachine)
-{}
-
-QScxmlFinalState::~QScxmlFinalState()
-{}
-
-void QScxmlFinalState::setAsInitialStateFor(QScxmlState *state)
-{
- state->setInitialState(this);
-}
-
-void QScxmlFinalState::setAsInitialStateFor(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->setInitialState(this);
-}
-
-QScxmlStateMachine *QScxmlFinalState::stateMachine() const {
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-QScxmlHistoryState::QScxmlHistoryState(QState *parent)
- : QHistoryState(parent)
-{
-}
-
-QScxmlHistoryState::~QScxmlHistoryState()
-{
-}
-
-void QScxmlHistoryState::setAsInitialStateFor(QScxmlState *state)
-{
- state->setInitialState(this);
-}
-
-void QScxmlHistoryState::setAsInitialStateFor(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->setInitialState(this);
-}
-
-QScxmlStateMachine *QScxmlHistoryState::stateMachine() const
-{
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-QScxmlExecutableContent::ContainerId QScxmlFinalState::doneData() const
-{
- Q_D(const QScxmlFinalState);
- return d->doneData;
-}
-
-void QScxmlFinalState::setDoneData(QScxmlExecutableContent::ContainerId doneData)
-{
- Q_D(QScxmlFinalState);
- d->doneData = doneData;
-}
-
-void QScxmlFinalState::setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlFinalState);
- d->onEntryInstructions = instructions;
-}
-
-void QScxmlFinalState::setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlFinalState);
- d->onExitInstructions = instructions;
-}
-
-void QScxmlFinalState::onEntry(QEvent *event)
-{
- Q_D(QScxmlFinalState);
-
- QFinalState::onEntry(event);
- auto smp = QScxmlStateMachinePrivate::get(stateMachine());
- smp->m_executionEngine->execute(d->onEntryInstructions);
-}
-
-void QScxmlFinalState::onExit(QEvent *event)
-{
- Q_D(QScxmlFinalState);
-
- QFinalState::onExit(event);
- QScxmlStateMachinePrivate::get(stateMachine())->m_executionEngine->execute(d->onExitInstructions);
-}
-
-QScxmlBaseTransition::QScxmlBaseTransition(QState *sourceState, const QStringList &eventSelector)
- : QAbstractTransition(*new QScxmlBaseTransitionPrivate, sourceState)
-{
- Q_D(QScxmlBaseTransition);
- d->eventSelector = eventSelector;
-}
-
-QScxmlBaseTransition::QScxmlBaseTransition(QScxmlBaseTransitionPrivate &dd, QState *parent,
- const QStringList &eventSelector)
- : QAbstractTransition(dd, parent)
-{
- Q_D(QScxmlBaseTransition);
- d->eventSelector = eventSelector;
-}
-
-QScxmlBaseTransition::~QScxmlBaseTransition()
-{}
-
-QScxmlStateMachine *QScxmlBaseTransition::stateMachine() const {
- if (QScxmlInternal::WrappedQStateMachine *t = qobject_cast<QScxmlInternal::WrappedQStateMachine *>(parent()))
- return t->stateMachine();
- if (QState *s = sourceState())
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(s->machine())->stateMachine();
- qCWarning(qscxmlLog) << "could not find Scxml::StateMachine in " << transitionLocation();
- return 0;
-}
-
-QString QScxmlBaseTransition::transitionLocation() const {
- if (QState *state = sourceState()) {
- QString stateName = state->objectName();
- int transitionIndex = state->transitions().indexOf(const_cast<QScxmlBaseTransition *>(this));
- return QStringLiteral("transition #%1 in state %2").arg(transitionIndex).arg(stateName);
- }
- return QStringLiteral("unbound transition @%1").arg(reinterpret_cast<quintptr>(this));
-}
-
-bool QScxmlBaseTransition::eventTest(QEvent *event)
-{
- Q_D(QScxmlBaseTransition);
-
- if (d->eventSelector.isEmpty())
- return true;
- if (event->type() == QEvent::None)
- return false;
- Q_ASSERT(stateMachine());
- QString eventName = QScxmlStateMachinePrivate::get(stateMachine())->m_event.name();
- bool selected = false;
- foreach (QString eventStr, d->eventSelector) {
- if (eventStr == QStringLiteral("*")) {
- selected = true;
- break;
- }
- if (eventStr.endsWith(QStringLiteral(".*")))
- eventStr.chop(2);
- if (eventName.startsWith(eventStr)) {
- QChar nextC = QLatin1Char('.');
- if (eventName.size() > eventStr.size())
- nextC = eventName.at(eventStr.size());
- if (nextC == QLatin1Char('.') || nextC == QLatin1Char('(')) {
- selected = true;
- break;
- }
- }
- }
- return selected;
-}
-
-void QScxmlBaseTransition::onTransition(QEvent *event)
-{
- Q_UNUSED(event);
-}
-
-QScxmlTransitionPrivate::QScxmlTransitionPrivate()
- : conditionalExp(QScxmlExecutableContent::NoEvaluator)
- , instructionsOnTransition(QScxmlExecutableContent::NoInstruction)
-{}
-
-QScxmlTransitionPrivate::~QScxmlTransitionPrivate()
-{}
-
-QScxmlTransition::QScxmlTransition(QState *sourceState, const QStringList &eventSelector)
- : QScxmlBaseTransition(*new QScxmlTransitionPrivate, sourceState, filterEmpty(eventSelector))
-{}
-
-QScxmlTransition::QScxmlTransition(const QStringList &eventSelector)
- : QScxmlBaseTransition(*new QScxmlTransitionPrivate, Q_NULLPTR, filterEmpty(eventSelector))
-{}
-
-QScxmlTransition::~QScxmlTransition()
-{}
-
-void QScxmlTransition::addTransitionTo(QScxmlState *state)
-{
- state->addTransition(this);
-}
-
-void QScxmlTransition::addTransitionTo(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->addTransition(this);
-}
-
-bool QScxmlTransition::eventTest(QEvent *event)
-{
- Q_D(QScxmlTransition);
-
-#ifdef DUMP_EVENT
- if (auto edm = dynamic_cast<QScxmlEcmaScriptDataModel *>(stateMachine()->dataModel()))
- qCDebug(qscxmlLog) << qPrintable(edm->engine()->evaluate(QLatin1String("JSON.stringify(_event)")).toString());
-#endif
-
- if (QScxmlBaseTransition::eventTest(event)) {
- bool ok = true;
- if (d->conditionalExp != QScxmlExecutableContent::NoEvaluator)
- return stateMachine()->dataModel()->evaluateToBool(d->conditionalExp, &ok) && ok;
- return true;
- }
-
- return false;
-}
-
-void QScxmlTransition::onTransition(QEvent *)
-{
- Q_D(QScxmlTransition);
-
- QScxmlStateMachinePrivate::get(stateMachine())->m_executionEngine->execute(d->instructionsOnTransition);
-}
-
-QScxmlStateMachine *QScxmlTransition::stateMachine() const {
- // work around a bug in QStateMachine
- if (QScxmlInternal::WrappedQStateMachine *t = qobject_cast<QScxmlInternal::WrappedQStateMachine *>(sourceState()))
- return t->stateMachine();
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-void QScxmlTransition::setInstructionsOnTransition(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlTransition);
- d->instructionsOnTransition = instructions;
-}
-
-void QScxmlTransition::setConditionalExpression(QScxmlExecutableContent::EvaluatorId evaluator)
-{
- Q_D(QScxmlTransition);
- d->conditionalExp = evaluator;
-}
-
-QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlqstates.h b/src/scxml/qscxmlqstates.h
deleted file mode 100644
index 7280a0f..0000000
--- a/src/scxml/qscxmlqstates.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef SCXMLQSTATES_H
-#define SCXMLQSTATES_H
-
-#include <QtScxml/qscxmlstatemachine.h>
-#include <QtScxml/qscxmlinvokableservice.h>
-
-#include <QAbstractTransition>
-#include <QFinalState>
-#include <QHistoryState>
-#include <QState>
-
-QT_BEGIN_NAMESPACE
-
-template<class T>
-class QScxmlInvokeScxmlFactory: public QScxmlInvokableScxmlServiceFactory
-{
-public:
- QScxmlInvokeScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool doAutoforward,
- const QVector<Param> &params,
- QScxmlExecutableContent::ContainerId finalize)
- : QScxmlInvokableScxmlServiceFactory(invokeLocation, id, idPrefix, idlocation, namelist,
- doAutoforward, params, finalize)
- {}
-
- QScxmlInvokableService *invoke(QScxmlStateMachine *parent) Q_DECL_OVERRIDE
- {
- return finishInvoke(new T, parent);
- }
-};
-
-class QScxmlStatePrivate;
-class Q_SCXML_EXPORT QScxmlState: public QState
-{
- Q_OBJECT
-
-public:
- QScxmlState(QState *parent = Q_NULLPTR);
- QScxmlState(QScxmlStateMachine *parent);
- ~QScxmlState();
-
- void setAsInitialStateFor(QScxmlState *state);
- void setAsInitialStateFor(QScxmlStateMachine *stateMachine);
-
- QScxmlStateMachine *stateMachine() const;
- QString stateLocation() const;
-
- void setInitInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setInvokableServiceFactories(const QVector<QScxmlInvokableServiceFactory *>& factories);
-
-Q_SIGNALS:
- void didEnter(); // TODO: REMOVE!
- void willExit(); // TODO: REMOVE!
-
-protected:
- void onEntry(QEvent * event) Q_DECL_OVERRIDE;
- void onExit(QEvent * event) Q_DECL_OVERRIDE;
-
-private:
- Q_DECLARE_PRIVATE(QScxmlState)
-};
-
-class QScxmlFinalStatePrivate;
-class Q_SCXML_EXPORT QScxmlFinalState: public QFinalState
-{
- Q_OBJECT
-
-public:
- QScxmlFinalState(QState *parent = Q_NULLPTR);
- QScxmlFinalState(QScxmlStateMachine *parent);
- ~QScxmlFinalState();
-
- void setAsInitialStateFor(QScxmlState *state);
- void setAsInitialStateFor(QScxmlStateMachine *stateMachine);
-
- QScxmlStateMachine *stateMachine() const;
-
- QScxmlExecutableContent::ContainerId doneData() const;
- void setDoneData(QScxmlExecutableContent::ContainerId doneData);
-
- void setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions);
-
-protected:
- void onEntry(QEvent * event) Q_DECL_OVERRIDE;
- void onExit(QEvent * event) Q_DECL_OVERRIDE;
-
-private:
- Q_DECLARE_PRIVATE(QScxmlFinalState)
-};
-
-class Q_SCXML_EXPORT QScxmlHistoryState: public QHistoryState
-{
- Q_OBJECT
-
-public:
- QScxmlHistoryState(QState *parent = Q_NULLPTR);
- ~QScxmlHistoryState();
-
- void setAsInitialStateFor(QScxmlState *state);
- void setAsInitialStateFor(QScxmlStateMachine *stateMachine);
-
- QScxmlStateMachine *stateMachine() const;
-};
-
-class QScxmlBaseTransitionPrivate;
-class Q_SCXML_EXPORT QScxmlBaseTransition: public QAbstractTransition
-{
- Q_OBJECT
- class Data;
-
-public:
- QScxmlBaseTransition(QState * sourceState = Q_NULLPTR,
- const QStringList &eventSelector = QStringList());
- ~QScxmlBaseTransition();
-
- QScxmlStateMachine *stateMachine() const;
- QString transitionLocation() const;
-
- bool eventTest(QEvent *event) Q_DECL_OVERRIDE;
-
-protected:
- void onTransition(QEvent *event) Q_DECL_OVERRIDE;
-
- QScxmlBaseTransition(QScxmlBaseTransitionPrivate &dd, QState *parent,
- const QStringList &eventSelector = QStringList());
-
-private:
- Q_DECLARE_PRIVATE(QScxmlBaseTransition)
-};
-
-class QScxmlTransitionPrivate;
-class Q_SCXML_EXPORT QScxmlTransition: public QScxmlBaseTransition
-{
- Q_OBJECT
-
-public:
- QScxmlTransition(QState * sourceState = Q_NULLPTR,
- const QStringList &eventSelector = QStringList());
- QScxmlTransition(const QStringList &eventSelector);
- ~QScxmlTransition();
-
- void addTransitionTo(QScxmlState *state);
- void addTransitionTo(QScxmlStateMachine *stateMachine);
-
- bool eventTest(QEvent *event) Q_DECL_OVERRIDE;
- QScxmlStateMachine *stateMachine() const;
-
- void setInstructionsOnTransition(QScxmlExecutableContent::ContainerId instructions);
- void setConditionalExpression(QScxmlExecutableContent::EvaluatorId evaluator);
-
-protected:
- void onTransition(QEvent *event) Q_DECL_OVERRIDE;
-
-private:
- Q_DECLARE_PRIVATE(QScxmlTransition)
-};
-
-QT_END_NAMESPACE
-
-#endif // SCXMLQSTATES_H
diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp
index 6603703..ef5a312 100644
--- a/src/scxml/qscxmlstatemachine.cpp
+++ b/src/scxml/qscxmlstatemachine.cpp
@@ -41,7 +41,6 @@
#include "qscxmlexecutablecontent_p.h"
#include "qscxmlevent_p.h"
#include "qscxmlinvokableservice.h"
-#include "qscxmlqstates_p.h"
#include "qscxmldatamodel_p.h"
#include <QAbstractState>
@@ -50,11 +49,9 @@
#include <QHash>
#include <QJSEngine>
#include <QLoggingCategory>
-#include <QState>
#include <QString>
#include <QTimer>
-
-#include <QtCore/private/qstatemachine_p.h>
+#include <QThread>
#include <functional>
@@ -63,232 +60,370 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qscxmlLog, "qt.scxml.statemachine")
Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
-namespace QScxmlInternal {
-class WrappedQStateMachinePrivate: public QStateMachinePrivate
-{
- Q_DECLARE_PUBLIC(WrappedQStateMachine)
-
-public:
- WrappedQStateMachinePrivate(QScxmlStateMachine *stateMachine)
- : m_stateMachine(stateMachine)
- , m_queuedEvents(Q_NULLPTR)
- {}
- ~WrappedQStateMachinePrivate()
- {
- if (m_queuedEvents) {
- foreach (const QueuedEvent &qt, *m_queuedEvents) {
- delete qt.event;
- }
+/*!
+ * \class QScxmlStateMachine
+ * \brief The QScxmlStateMachine class provides an interface to the state machines
+ * created from SCXML files.
+ * \since 5.7
+ * \inmodule QtScxml
+ *
+ * QScxmlStateMachine is an implementation of the
+ * \l{SCXML Specification}{State Chart XML (SCXML)}.
+ *
+ * All states that are defined in the SCXML file
+ * are accessible as properties of QScxmlStateMachine.
+ * These properties are boolean values and indicate
+ * whether the state is active or inactive.
+ *
+ * \note The QScxmlStateMachine needs a QEventLoop to work correctly. The event loop is used to
+ * implement the \c delay attribute for events and to schedule the processing of a state
+ * machine when events are received from nested (or parent) state machines.
+ */
- delete m_queuedEvents;
- }
- }
+/*!
+ \fn QScxmlStateMachine::connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver,
+ PointerToMemberFunction method,
+ Qt::ConnectionType type)
- int eventIdForDelayedEvent(const QString &sendId);
-
- QScxmlStateMachine *stateMachine() const
- { return m_stateMachine; }
-
- QScxmlStateMachinePrivate *stateMachinePrivate() const
- { return QScxmlStateMachinePrivate::get(stateMachine()); }
-
-protected: // overrides for QStateMachinePrivate:
- void noMicrostep() Q_DECL_OVERRIDE;
- void processedPendingEvents(bool didChange) Q_DECL_OVERRIDE;
- void beginMacrostep() Q_DECL_OVERRIDE;
- void endMacrostep(bool didChange) Q_DECL_OVERRIDE;
-
- void enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted,
- const QList<QAbstractState*> &statesToEnter_sorted,
- const QSet<QAbstractState*> &statesForDefaultEntry,
- QHash<QAbstractState *, QVector<QPropertyAssignment> > &propertyAssignmentsForState
-# ifndef QT_NO_ANIMATION
- , const QList<QAbstractAnimation*> &selectedAnimations
-# endif
- ) Q_DECL_OVERRIDE;
- void exitStates(QEvent *event, const QList<QAbstractState *> &statesToExit_sorted,
- const QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates) Q_DECL_OVERRIDE;
-
- void exitInterpreter() Q_DECL_OVERRIDE;
-
- void emitStateFinished(QState *forState, QFinalState *guiltyState) Q_DECL_OVERRIDE;
- void startupHook() Q_DECL_OVERRIDE;
-
-public: // fields
- QScxmlStateMachine *m_stateMachine;
-
- struct QueuedEvent
- {
- QueuedEvent(QEvent *event = Q_NULLPTR, QStateMachine::EventPriority priority = QStateMachine::NormalPriority)
- : event(event)
- , priority(priority)
- {}
-
- QEvent *event;
- QStateMachine::EventPriority priority;
- };
- QVector<QueuedEvent> *m_queuedEvents;
-};
-
-WrappedQStateMachine::WrappedQStateMachine(QScxmlStateMachine *parent)
- : QStateMachine(*new WrappedQStateMachinePrivate(parent), parent)
-{}
+ Creates a connection of the given \a type from the event specified by
+ \a scxmlEventSpec to \a method in the \a receiver object.
-WrappedQStateMachine::WrappedQStateMachine(WrappedQStateMachinePrivate &dd, QScxmlStateMachine *parent)
- : QStateMachine(dd, parent)
-{}
+ The receiver's \a method must take a QScxmlEvent as a parameter.
-QScxmlStateMachine *WrappedQStateMachine::stateMachine() const
-{
- Q_D(const WrappedQStateMachine);
+ In contrast to event specifications in SCXML documents, spaces are not
+ allowed in the \a scxmlEventSpec here. In order to connect to multiple
+ events with different prefixes, connectToEvent() has to be called multiple
+ times.
- return d->stateMachine();
-}
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
-QScxmlStateMachinePrivate *WrappedQStateMachine::stateMachinePrivate()
-{
- Q_D(const WrappedQStateMachine);
+/*!
+ \fn QScxmlStateMachine::connectToEvent(const QString &scxmlEventSpec,
+ Functor functor,
+ Qt::ConnectionType type)
- return d->stateMachinePrivate();
-}
-} // Internal namespace
+ Creates a connection of the given \a type from the event specified by
+ \a scxmlEventSpec to \a functor.
+
+ The \a functor must take a QScxmlEvent as a parameter.
+
+ In contrast to event specifications in SCXML documents, spaces are not
+ allowed in the \a scxmlEventSpec here. In order to connect to multiple
+ events with different prefixes, connectToEvent() has to be called multiple
+ times.
+
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
/*!
- * \class QScxmlEventFilter
- * \brief The QScxmlEventFilter class is an event filter for an SCXML state machine.
- * \since 5.7
- * \inmodule QtScxml
- *
- * An event filter can be used to intercept events generated by an SCXML state machine. By default,
- * the QScxmlStateMachine will have an event filter that will intercept events that are marked as
- * external and that have the type \c qt:signal to emit signals.
- *
- * \sa QScxmlStateMachine
+ \fn QScxmlStateMachine::connectToEvent(const QString &scxmlEventSpec,
+ const QObject *context,
+ Functor functor,
+ Qt::ConnectionType type)
+
+ Creates a connection of the given \a type from the event specified by
+ \a scxmlEventSpec to \a functor using \a context as context.
+
+ The \a functor must take a QScxmlEvent as a parameter.
+
+ In contrast to event specifications in SCXML documents, spaces are not
+ allowed in the \a scxmlEventSpec here. In order to connect to multiple
+ events with different prefixes, connectToEvent() has to be called multiple
+ times.
+
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
+
+/*!
+ \fn QScxmlStateMachine::connectToState(const QString &scxmlStateName,
+ const QObject *receiver,
+ PointerToMemberFunction method,
+ Qt::ConnectionType type)
+
+ Creates a connection of the given \a type from the state specified by
+ \a scxmlStateName to \a method in the \a receiver object.
+
+ The receiver's \a method must take a boolean argument that indicates
+ whether the state connected became active or inactive.
+
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
+
+
+/*!
+ \fn QScxmlStateMachine::connectToState(const QString &scxmlStateName,
+ Functor functor,
+ Qt::ConnectionType type)
+
+ Creates a connection of the given \a type from the state specified by
+ \a scxmlStateName to \a functor.
+
+ The \a functor must take a boolean argument that indicates whether the
+ state connected became active or inactive.
+
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
+
+/*!
+ \fn QScxmlStateMachine::connectToState(const QString &scxmlStateName,
+ const QObject *context,
+ Functor functor,
+ Qt::ConnectionType type)
+
+ Creates a connection of the given \a type from the state specified by
+ \a scxmlStateName to \a functor using \a context as context.
+
+ The \a functor must take a boolean argument that indicates whether the
+ state connected became active or inactive.
+
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
+
+/*!
+ \fn QScxmlStateMachine::onEntry(const QObject *receiver,
+ const char *method)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver using QMetaObject::invokeMethod() if that argument
+ is \c true and \a receiver has not been deleted, yet.
+
+ The given \a method must not accept any arguments. \a method is the plain
+ method name, not enclosed in \c SIGNAL() or \c SLOT().
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is entered.
*/
/*!
- * Destroys the SCXML event filter.
+ \fn QScxmlStateMachine::onExit(const QObject *receiver, const char *method)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver using QMetaObject::invokeMethod() if that argument
+ is \c false and \a receiver has not been deleted, yet.
+
+ The given \a method must not accept any arguments. \a method is the plain
+ method name, not enclosed in SIGNAL(...) or SLOT(...).
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is left.
*/
-QScxmlEventFilter::~QScxmlEventFilter()
-{}
/*!
- * \fn QScxmlEventFilter::handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine)
- *
- * Returns \c true if the \a event should be submitted to the \a stateMachine
- * and it was not intercepted inside the event filter, \c false otherwise.
+ \fn QScxmlStateMachine::onEntry(Functor functor)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a functor if that argument is \c true. The given \a functor must not
+ accept any arguments.
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is entered.
*/
/*!
- * \class QScxmlStateMachine
- * \brief The QScxmlStateMachine class provides an interface to the state machines
- * created from SCXML files.
- * \since 5.7
- * \inmodule QtScxml
- *
- * QScxmlStateMachine is an implementation of the
- * \l{SCXML Specification}{State Chart XML (SCXML)}.
- *
- * All states that are defined in the SCXML file
- * are accessible as properties of QScxmlStateMachine.
- * These properties are boolean values and indicate
- * whether the state is active or inactive.
- *
- * All external signals defined inside the SCXML file
- * that are of the \c qt:signal type, are accessible
- * as signals of QScxmlStateMachine in the \e {Qt mode}.
- * The only argument of these signals
- * is always QVariant, which is of QMap<QString, QVariant>
- * type containing the content of all the \c <param>
- * elements specified as children of a \c <send> element.
- * The name of each QScxmlStateMachine signal
- * corresponds to the value defined in the
- * \e event attribute of one \c <send> tag in the SCXML file.
+ \fn QScxmlStateMachine::onExit(Functor functor)
+
+ Returns a functor that accepts a boolean argument and calls the given
+ \a functor if that argument is \c false. The given \a functor must not
+ accept any arguments.
+
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is left.
*/
/*!
- \fn QScxmlStateMachine::eventOccurred(const QScxmlEvent &event)
+ \fn QScxmlStateMachine::onEntry(const QObject *receiver,
+ PointerToMemberFunction method)
- This signal is emitted when the SCXML event \a event occurs. This signal is
- emitted for all events.
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver if that argument is \c true and the \a receiver
+ has not been deleted, yet. The given \a method must not accept any
+ arguments.
- \sa externalEventOccurred()
-*/
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is entered.
+ */
/*!
- \fn QScxmlStateMachine::externalEventOccurred(const QScxmlEvent &event)
+ \fn QScxmlStateMachine::onExit(const QObject *receiver,
+ PointerToMemberFunction method)
- This signal is emitted for each \c <send> element in the SCXML file that
- contains the attribute \c {type="qt:signal"}. The event that occurred is
- specified by \a event.
+ Returns a functor that accepts a boolean argument and calls the given
+ \a method on \a receiver if that argument is \c false and the \a receiver
+ has not been deleted, yet. The given \a method must not accept any
+ arguments.
- \sa eventOccurred()
-*/
+ This is useful to wrap handlers for connectToState() that should only
+ be executed when the state is left.
+ */
-QAtomicInt QScxmlStateMachinePrivate::m_sessionIdCounter = QAtomicInt(0);
+namespace QScxmlInternal {
+static int signalIndex(const QMetaObject *meta, const QByteArray &signalName)
+{
+ Q_ASSERT(meta);
-QScxmlStateMachinePrivate::QScxmlStateMachinePrivate()
- : QObjectPrivate()
- , m_sessionId(QScxmlStateMachine::generateSessionId(QStringLiteral("session-")))
- , m_isInvoked(false)
- , m_isInitialized(false)
- , m_dataModel(Q_NULLPTR)
- , m_dataBinding(QScxmlStateMachine::EarlyBinding)
- , m_executionEngine(Q_NULLPTR)
- , m_tableData(Q_NULLPTR)
- , m_qStateMachine(Q_NULLPTR)
- , m_eventFilter(Q_NULLPTR)
- , m_parentStateMachine(Q_NULLPTR)
-{}
+ int signalIndex = meta->indexOfSignal(signalName.constData());
-QScxmlStateMachinePrivate::~QScxmlStateMachinePrivate()
+ // If signal doesn't exist, return negative value
+ if (signalIndex < 0)
+ return signalIndex;
+
+ // signal belongs to class whose meta object was passed, not some derived class.
+ Q_ASSERT(meta->methodOffset() <= signalIndex);
+
+ // Duplicate of computeOffsets in qobject.cpp
+ const QMetaObject *m = meta->d.superdata;
+ while (m) {
+ const QMetaObjectPrivate *d = QMetaObjectPrivate::get(m);
+ signalIndex = signalIndex - d->methodCount + d->signalCount;
+ m = m->d.superdata;
+ }
+
+ // Asserting about the signal not being cloned would be nice, too, but not practical.
+
+ return signalIndex;
+}
+
+void EventLoopHook::queueProcessEvents()
{
- qDeleteAll(m_invokedServices);
- delete m_executionEngine;
+ if (smp->m_isProcessingEvents)
+ return;
+
+ QMetaObject::invokeMethod(this, "doProcessEvents", Qt::QueuedConnection);
}
-void QScxmlStateMachinePrivate::init()
+void EventLoopHook::doProcessEvents()
{
- Q_Q(QScxmlStateMachine);
- m_executionEngine = new QScxmlExecutableContent::QScxmlExecutionEngine(q);
- setQStateMachine(new QScxmlInternal::WrappedQStateMachine(q));
- QObject::connect(m_qStateMachine, &QStateMachine::runningChanged,
- q, &QScxmlStateMachine::runningChanged);
- QObject::connect(m_qStateMachine, &QStateMachine::finished,
- q, &QScxmlStateMachine::finished);
+ smp->processEvents();
+}
- // The final state is also a stable state.
- QObject::connect(m_qStateMachine, &QStateMachine::finished,
- q, &QScxmlStateMachine::reachedStableState);
+void EventLoopHook::timerEvent(QTimerEvent *timerEvent)
+{
+ const int timerId = timerEvent->timerId();
+ for (auto it = smp->m_delayedEvents.begin(), eit = smp->m_delayedEvents.end(); it != eit; ++it) {
+ if (it->first == timerId) {
+ QScxmlEvent *scxmlEvent = it->second;
+ smp->m_delayedEvents.erase(it);
+ smp->routeEvent(scxmlEvent);
+ return;
+ }
+ }
+}
+
+void ScxmlEventRouter::route(const QStringList &segments, QScxmlEvent *event)
+{
+ emit eventOccurred(*event);
+ if (!segments.isEmpty()) {
+ auto it = children.find(segments.first());
+ if (it != children.end())
+ it.value()->route(segments.mid(1), event);
+ }
+}
+
+static QString nextSegment(const QStringList &segments)
+{
+ if (segments.isEmpty())
+ return QString();
+
+ const QString &segment = segments.first();
+ return segment == QLatin1String("*") ? QString() : segment;
}
-void QScxmlStateMachinePrivate::setQStateMachine(QScxmlInternal::WrappedQStateMachine *stateMachine)
+ScxmlEventRouter *ScxmlEventRouter::child(const QString &segment)
{
- m_qStateMachine = stateMachine;
+ ScxmlEventRouter *&child = children[segment];
+ if (child == nullptr)
+ child = new ScxmlEventRouter(this);
+ return child;
}
-static QAbstractState *findState(const QString &scxmlName, QStateMachine *parent)
+void ScxmlEventRouter::disconnectNotify(const QMetaMethod &signal)
{
- QList<QObject *> worklist;
- worklist.reserve(parent->children().size() + parent->configuration().size());
- worklist.append(parent);
+ Q_UNUSED(signal);
+
+ // Defer the actual work, as this may be called from a destructor, or the signal may not
+ // actually be disconnected, yet.
+ QTimer::singleShot(0, this, [this] {
+ if (!children.isEmpty() || receivers(SIGNAL(eventOccurred(QScxmlEvent))) > 0)
+ return;
+
+ ScxmlEventRouter *parentRouter = qobject_cast<ScxmlEventRouter *>(parent());
+ if (!parentRouter) // root node
+ return;
- while (!worklist.isEmpty()) {
- QObject *obj = worklist.takeLast();
- if (QAbstractState *state = qobject_cast<QAbstractState *>(obj)) {
- if (state->objectName() == scxmlName)
- return state;
+ QHash<QString, ScxmlEventRouter *>::Iterator it = parentRouter->children.begin(),
+ end = parentRouter->children.end();
+ for (; it != end; ++it) {
+ if (it.value() == this) {
+ parentRouter->children.erase(it);
+ parentRouter->disconnectNotify(QMetaMethod());
+ break;
+ }
}
- worklist.append(obj->children());
- }
- return Q_NULLPTR;
+ deleteLater(); // The parent might delete itself, triggering QObject delete cascades.
+ });
}
-QAbstractState *QScxmlStateMachinePrivate::stateByScxmlName(const QString &scxmlName)
+QMetaObject::Connection ScxmlEventRouter::connectToEvent(const QStringList &segments,
+ const QObject *receiver,
+ const char *method,
+ Qt::ConnectionType type)
{
- return findState(scxmlName, m_qStateMachine);
+ QString segment = nextSegment(segments);
+ return segment.isEmpty() ?
+ connect(this, SIGNAL(eventOccurred(QScxmlEvent)), receiver, method, type) :
+ child(segment)->connectToEvent(segments.mid(1), receiver, method, type);
+}
+
+QMetaObject::Connection ScxmlEventRouter::connectToEvent(const QStringList &segments,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *method,
+ Qt::ConnectionType type)
+{
+ QString segment = nextSegment(segments);
+ if (segment.isEmpty()) {
+ const int *types = Q_NULLPTR;
+ if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
+ types = QtPrivate::ConnectionTypes<QtPrivate::List<QScxmlEvent> >::types();
+
+ const QMetaObject *meta = metaObject();
+ static const int eventOccurredIndex = signalIndex(meta, "eventOccurred(QScxmlEvent)");
+ return QObjectPrivate::connectImpl(this, eventOccurredIndex, receiver, slot, method, type,
+ types, meta);
+ } else {
+ return child(segment)->connectToEvent(segments.mid(1), receiver, slot, method, type);
+ }
+}
+
+} // namespace QScxmlInternal
+
+QAtomicInt QScxmlStateMachinePrivate::m_sessionIdCounter = QAtomicInt(0);
+
+QScxmlStateMachinePrivate::QScxmlStateMachinePrivate(const QMetaObject *metaObject)
+ : QObjectPrivate()
+ , m_sessionId(QScxmlStateMachinePrivate::generateSessionId(QStringLiteral("session-")))
+ , m_isInvoked(false)
+ , m_isInitialized(false)
+ , m_isProcessingEvents(false)
+ , m_dataModel(Q_NULLPTR)
+ , m_loader(&m_defaultLoader)
+ , m_executionEngine(Q_NULLPTR)
+ , m_tableData(Q_NULLPTR)
+ , m_parentStateMachine(Q_NULLPTR)
+ , m_eventLoopHook(this)
+ , m_metaObject(metaObject)
+ , m_infoSignalProxy(nullptr)
+{}
+
+QScxmlStateMachinePrivate::~QScxmlStateMachinePrivate()
+{
+ for (const InvokedService &invokedService : m_invokedServices)
+ delete invokedService.service;
+ qDeleteAll(m_cachedFactories);
+ delete m_executionEngine;
}
QScxmlStateMachinePrivate::ParserData *QScxmlStateMachinePrivate::parserData()
@@ -298,23 +433,51 @@ QScxmlStateMachinePrivate::ParserData *QScxmlStateMachinePrivate::parserData()
return m_parserData.data();
}
-void QScxmlStateMachinePrivate::addService(QScxmlInvokableService *service)
+void QScxmlStateMachinePrivate::addService(int invokingState)
{
Q_Q(QScxmlStateMachine);
- Q_ASSERT(!m_invokedServices.contains(service));
- m_invokedServices.append(service);
- q->setService(service->name(), service);
+
+ const int arrayId = m_stateTable->state(invokingState).serviceFactoryIds;
+ if (arrayId == StateTable::InvalidIndex)
+ return;
+
+ const auto &ids = m_stateTable->array(arrayId);
+ for (int id : ids) {
+ auto factory = serviceFactory(id);
+ auto service = factory->invoke(q);
+ if (service == nullptr)
+ 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());
+ }
+ emitInvokedServicesChanged();
}
-bool QScxmlStateMachinePrivate::removeService(QScxmlInvokableService *service)
+void QScxmlStateMachinePrivate::removeService(int invokingState)
{
- Q_Q(QScxmlStateMachine);
- Q_ASSERT(m_invokedServices.contains(service));
- if (m_invokedServices.removeOne(service)) {
- q->setService(service->name(), Q_NULLPTR);
- return true;
+ const int arrayId = m_stateTable->state(invokingState).serviceFactoryIds;
+ if (arrayId == StateTable::InvalidIndex)
+ return;
+
+ for (size_t i = 0, ei = m_invokedServices.size(); i != ei; ++i) {
+ auto &it = m_invokedServices[i];
+ QScxmlInvokableService *service = it.service;
+ if (it.invokingState == invokingState && service != nullptr) {
+ it.service = nullptr;
+ delete service;
+ }
}
- return false;
+ emitInvokedServicesChanged();
+}
+
+QScxmlInvokableServiceFactory *QScxmlStateMachinePrivate::serviceFactory(int id)
+{
+ Q_ASSERT(id <= m_stateTable->maxServiceId && id >= 0);
+ QScxmlInvokableServiceFactory *& factory = m_cachedFactories[size_t(id)];
+ if (factory == nullptr)
+ factory = m_tableData->serviceFactory(id);
+ return factory;
}
bool QScxmlStateMachinePrivate::executeInitialSetup()
@@ -341,11 +504,14 @@ void QScxmlStateMachinePrivate::routeEvent(QScxmlEvent *event)
} else if (origin.startsWith(QStringLiteral("#_")) && origin != QStringLiteral("#_internal")) {
// route to children
auto originId = origin.midRef(2);
- foreach (QScxmlInvokableService *service, m_invokedServices) {
+ for (const auto &invokedService : m_invokedServices) {
+ auto service = invokedService.service;
+ if (service == nullptr)
+ continue;
if (service->id() == originId) {
qCDebug(qscxmlLog) << q << "routing event" << event->name()
- << "from" << q->name()
- << "to parent" << service->id();
+ << "from" << q->name()
+ << "to child" << service->id();
service->postEvent(new QScxmlEvent(*event));
}
}
@@ -359,17 +525,58 @@ void QScxmlStateMachinePrivate::postEvent(QScxmlEvent *event)
{
Q_Q(QScxmlStateMachine);
- QStateMachine::EventPriority priority =
- event->eventType() == QScxmlEvent::ExternalEvent ? QStateMachine::NormalPriority
- : QStateMachine::HighPriority;
+ if (!event->name().startsWith(QStringLiteral("done.invoke."))) {
+ for (int id = 0, end = static_cast<int>(m_invokedServices.size()); id != end; ++id) {
+ auto service = m_invokedServices[id].service;
+ if (service == nullptr)
+ continue;
+ auto factory = serviceFactory(id);
+ if (event->invokeId() == service->id()) {
+ setEvent(event);
+ service->finalize(factory->invokeInfo().finalize);
+ resetEvent();
+ }
+ if (factory->invokeInfo().autoforward) {
+ qCDebug(qscxmlLog) << q << "auto-forwarding event" << event->name()
+ << "from" << q->name()
+ << "to child" << service->id();
+ service->postEvent(new QScxmlEvent(*event));
+ }
+ }
+ }
+
+ if (event->eventType() == QScxmlEvent::ExternalEvent)
+ m_router.route(event->name().split(QLatin1Char('.')), event);
- if (m_qStateMachine->isRunning()) {
- qCDebug(qscxmlLog) << q << "posting event" << event->name();
- m_qStateMachine->postEvent(event, priority);
+ if (event->eventType() == QScxmlEvent::ExternalEvent) {
+ qCDebug(qscxmlLog) << q << "posting external event" << event->name();
+ m_externalQueue.enqueue(event);
} else {
- qCDebug(qscxmlLog) << q << "queueing event" << event->name();
- m_qStateMachine->queueEvent(event, priority);
+ qCDebug(qscxmlLog) << q << "posting internal event" << event->name();
+ m_internalQueue.enqueue(event);
}
+
+ m_eventLoopHook.queueProcessEvents();
+}
+
+void QScxmlStateMachinePrivate::submitDelayedEvent(QScxmlEvent *event)
+{
+ Q_ASSERT(event);
+ Q_ASSERT(event->delay() > 0);
+
+ const int timerId = m_eventLoopHook.startTimer(event->delay());
+ if (timerId == 0) {
+ qWarning("QScxmlStateMachinePrivate::submitDelayedEvent: "
+ "failed to start timer for event '%s' (%p)",
+ qPrintable(event->name()), event);
+ delete event;
+ return;
+ }
+ m_delayedEvents.push_back(std::make_pair(timerId, event));
+
+ qCDebug(qscxmlLog) << q_func()
+ << ": delayed event" << event->name()
+ << "(" << event << ") got id:" << timerId;
}
/*!
@@ -390,6 +597,797 @@ void QScxmlStateMachinePrivate::submitError(const QString &type, const QString &
q->submitEvent(QScxmlEventBuilder::errorEvent(q, type, msg, sendid));
}
+void QScxmlStateMachinePrivate::start()
+{
+ if (m_stateTable->binding == StateTable::LateBinding)
+ m_isFirstStateEntry.resize(m_stateTable->stateCount, true);
+
+ m_runningState = Starting;
+ Q_ASSERT(m_stateTable->initialTransition != StateTable::InvalidIndex);
+}
+
+void QScxmlStateMachinePrivate::pause()
+{
+ if (isRunnable() && !isPaused())
+ m_runningState = Paused;
+}
+
+void QScxmlStateMachinePrivate::processEvents()
+{
+ if (m_isProcessingEvents || (!isRunnable() && !isPaused()))
+ return;
+
+ m_isProcessingEvents = true;
+
+ Q_Q(QScxmlStateMachine);
+ qCDebug(qscxmlLog) << q_func() << "starting macrostep";
+
+ while (isRunnable() && !isPaused()) {
+ if (m_runningState == Starting) {
+ enterStates({m_stateTable->initialTransition});
+ if (m_runningState == Starting)
+ m_runningState = Running;
+ continue;
+ }
+
+ OrderedSet enabledTransitions;
+ std::vector<int> configurationInDocumentOrder = m_configuration.list();
+ std::sort(configurationInDocumentOrder.begin(), configurationInDocumentOrder.end());
+ selectTransitions(enabledTransitions, configurationInDocumentOrder, nullptr);
+ if (!enabledTransitions.isEmpty()) {
+ microstep(enabledTransitions);
+ } else if (!m_internalQueue.isEmpty()) {
+ auto event = m_internalQueue.dequeue();
+ setEvent(event);
+ selectTransitions(enabledTransitions, configurationInDocumentOrder, event);
+ if (!enabledTransitions.isEmpty()) {
+ microstep(enabledTransitions);
+ }
+ resetEvent();
+ delete event;
+ } else if (!m_externalQueue.isEmpty()) {
+ auto event = m_externalQueue.dequeue();
+ setEvent(event);
+ selectTransitions(enabledTransitions, configurationInDocumentOrder, event);
+ if (!enabledTransitions.isEmpty()) {
+ microstep(enabledTransitions);
+ }
+ resetEvent();
+ delete event;
+ } else {
+ // nothing to do, so:
+ break;
+ }
+ }
+
+ if (!m_statesToInvoke.empty()) {
+ for (int stateId : m_statesToInvoke)
+ addService(stateId);
+ m_statesToInvoke.clear();
+ }
+
+ qCDebug(qscxmlLog) << q_func()
+ << "finished macrostep, runnable:" << isRunnable()
+ << "paused:" << isPaused();
+ emit q->reachedStableState();
+ if (!isRunnable() && !isPaused()) {
+ exitInterpreter();
+ emit q->finished();
+ }
+
+ m_isProcessingEvents = false;
+}
+
+void QScxmlStateMachinePrivate::setEvent(QScxmlEvent *event)
+{
+ Q_ASSERT(event);
+ m_dataModel->setScxmlEvent(*event);
+}
+
+void QScxmlStateMachinePrivate::resetEvent()
+{
+ m_dataModel->setScxmlEvent(QScxmlEvent());
+}
+
+void QScxmlStateMachinePrivate::emitStateActive(int stateIndex, bool active)
+{
+ Q_Q(QScxmlStateMachine);
+ void *args[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&active)) };
+ QMetaObject::activate(q, m_metaObject, stateIndex, args);
+}
+
+void QScxmlStateMachinePrivate::emitInvokedServicesChanged()
+{
+ Q_Q(QScxmlStateMachine);
+ emit q->invokedServicesChanged(q->invokedServices());
+}
+
+void QScxmlStateMachinePrivate::attach(QScxmlStateMachineInfo *info)
+{
+ Q_Q(QScxmlStateMachine);
+
+ if (!m_infoSignalProxy)
+ m_infoSignalProxy = new QScxmlInternal::StateMachineInfoProxy(q);
+
+ QObject::connect(m_infoSignalProxy, &QScxmlInternal::StateMachineInfoProxy::statesEntered,
+ info, &QScxmlStateMachineInfo::statesEntered);
+ QObject::connect(m_infoSignalProxy, &QScxmlInternal::StateMachineInfoProxy::statesExited,
+ info, &QScxmlStateMachineInfo::statesExited);
+ QObject::connect(m_infoSignalProxy,&QScxmlInternal::StateMachineInfoProxy::transitionsTriggered,
+ info, &QScxmlStateMachineInfo::transitionsTriggered);
+}
+
+QStringList QScxmlStateMachinePrivate::stateNames(const std::vector<int> &stateIndexes) const
+{
+ QStringList names;
+ for (int idx : stateIndexes)
+ names.append(m_tableData->string(m_stateTable->state(idx).name));
+ return names;
+}
+
+std::vector<int> QScxmlStateMachinePrivate::historyStates(int stateIdx) const {
+ const StateTable::Array kids = m_stateTable->array(m_stateTable->state(stateIdx).childStates);
+ std::vector<int> res;
+ if (!kids.isValid()) return res;
+ for (int k : kids) {
+ if (m_stateTable->state(k).isHistoryState())
+ res.push_back(k);
+ }
+ return res;
+}
+
+void QScxmlStateMachinePrivate::exitInterpreter()
+{
+ qCDebug(qscxmlLog) << q_func() << "exiting SCXML processing";
+
+ for (auto it : m_delayedEvents) {
+ m_eventLoopHook.killTimer(it.first);
+ delete it.second;
+ }
+ m_delayedEvents.clear();
+
+ auto statesToExitSorted = m_configuration.list();
+ std::sort(statesToExitSorted.begin(), statesToExitSorted.end(), std::greater<int>());
+ for (int stateIndex : statesToExitSorted) {
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.exitInstructions != StateTable::InvalidIndex) {
+ m_executionEngine->execute(state.exitInstructions);
+ }
+ removeService(stateIndex);
+ if (state.type == StateTable::State::Final && state.parentIsScxmlElement()) {
+ returnDoneEvent(state.doneData);
+ }
+ }
+}
+
+void QScxmlStateMachinePrivate::returnDoneEvent(QScxmlExecutableContent::ContainerId doneData)
+{
+ Q_Q(QScxmlStateMachine);
+
+ m_executionEngine->execute(doneData, QVariant());
+ if (m_isInvoked) {
+ auto e = new QScxmlEvent;
+ e->setName(QStringLiteral("done.invoke.") + q->sessionId());
+ e->setInvokeId(q->sessionId());
+ QScxmlStateMachinePrivate::get(m_parentStateMachine)->postEvent(e);
+ }
+}
+
+bool QScxmlStateMachinePrivate::nameMatch(const StateTable::Array &patterns,
+ QScxmlEvent *event) const
+{
+ const QString eventName = event->name();
+ bool selected = false;
+ for (int eventSelectorIter = 0; eventSelectorIter < patterns.size(); ++eventSelectorIter) {
+ QString eventStr = m_tableData->string(patterns[eventSelectorIter]);
+ if (eventStr == QStringLiteral("*")) {
+ selected = true;
+ break;
+ }
+ if (eventStr.endsWith(QStringLiteral(".*")))
+ eventStr.chop(2);
+ if (eventName.startsWith(eventStr)) {
+ QChar nextC = QLatin1Char('.');
+ if (eventName.size() > eventStr.size())
+ nextC = eventName.at(eventStr.size());
+ if (nextC == QLatin1Char('.') || nextC == QLatin1Char('(')) {
+ selected = true;
+ break;
+ }
+ }
+ }
+ return selected;
+}
+
+void QScxmlStateMachinePrivate::selectTransitions(OrderedSet &enabledTransitions,
+ const std::vector<int> &configInDocumentOrder,
+ QScxmlEvent *event) const
+{
+ if (event == nullptr) {
+ qCDebug(qscxmlLog) << q_func() << "selectEventlessTransitions";
+ } else {
+ qCDebug(qscxmlLog) << q_func() << "selectTransitions with event"
+ << QScxmlEventPrivate::debugString(event).constData();
+ }
+
+ std::vector<int> states;
+ states.reserve(16);
+ for (int configStateIdx : configInDocumentOrder) {
+ if (m_stateTable->state(configStateIdx).isAtomic()) {
+ states.clear();
+ states.push_back(configStateIdx);
+ getProperAncestors(&states, configStateIdx, -1);
+ for (int stateIdx : states) {
+ bool finishedWithThisConfigState = false;
+
+ if (stateIdx == -1) {
+ // the state machine has no transitions (other than the initial one, which has
+ // already been taken at this point)
+ continue;
+ }
+ const auto &state = m_stateTable->state(stateIdx);
+ const StateTable::Array transitions = m_stateTable->array(state.transitions);
+ if (!transitions.isValid())
+ continue;
+ std::vector<int> sortedTransitions(transitions.size(), -1);
+ std::copy(transitions.begin(), transitions.end(), sortedTransitions.begin());
+ for (int transitionIndex : sortedTransitions) {
+ const StateTable::Transition &t = m_stateTable->transition(transitionIndex);
+ bool enabled = false;
+ if (event == nullptr) {
+ if (t.events == -1) {
+ if (t.condition == -1) {
+ enabled = true;
+ } else {
+ bool ok = false;
+ enabled = m_dataModel->evaluateToBool(t.condition, &ok) && ok;
+ }
+ }
+ } else {
+ if (t.events != -1 && nameMatch(m_stateTable->array(t.events), event)) {
+ if (t.condition == -1) {
+ enabled = true;
+ } else {
+ bool ok = false;
+ enabled = m_dataModel->evaluateToBool(t.condition, &ok) && ok;
+ }
+ }
+ }
+ if (enabled) {
+ enabledTransitions.add(transitionIndex);
+ finishedWithThisConfigState = true;
+ break; // stop iterating over transitions
+ }
+ }
+
+ if (finishedWithThisConfigState)
+ break; // stop iterating over ancestors
+ }
+ }
+ }
+ if (!enabledTransitions.isEmpty())
+ removeConflictingTransitions(&enabledTransitions);
+}
+
+void QScxmlStateMachinePrivate::removeConflictingTransitions(OrderedSet *enabledTransitions) const
+{
+ Q_ASSERT(enabledTransitions);
+
+ auto sortedTransitions = enabledTransitions->takeList();
+ std::sort(sortedTransitions.begin(), sortedTransitions.end(), [this](int t1, int t2) -> bool {
+ auto descendantDepth = [this](int state, int ancestor)->int {
+ int depth = 0;
+ for (int it = state; it != -1; it = m_stateTable->state(it).parent) {
+ if (it == ancestor)
+ break;
+ ++depth;
+ }
+ return depth;
+ };
+
+ const auto &s1 = m_stateTable->transition(t1).source;
+ const auto &s2 = m_stateTable->transition(t2).source;
+ if (s1 == s2) {
+ return t1 < t2;
+ } else if (isDescendant(s1, s2)) {
+ return true;
+ } else if (isDescendant(s2, s1)) {
+ return false;
+ } else {
+ const int lcca = findLCCA({ s1, s2 });
+ const int s1Depth = descendantDepth(s1, lcca);
+ const int s2Depth = descendantDepth(s2, lcca);
+ if (s1Depth == s2Depth)
+ return s1 < s2;
+ else
+ return s1Depth > s2Depth;
+ }
+ });
+
+ OrderedSet filteredTransitions;
+ for (int t1 : sortedTransitions) {
+ OrderedSet transitionsToRemove;
+ bool t1Preempted = false;
+ OrderedSet exitSetT1;
+ computeExitSet({t1}, exitSetT1);
+ const int source1 = m_stateTable->transition(t1).source;
+ for (int t2 : filteredTransitions) {
+ OrderedSet exitSetT2;
+ computeExitSet({t2}, exitSetT2);
+ if (exitSetT1.intersectsWith(exitSetT2)) {
+ const int source2 = m_stateTable->transition(t2).source;
+ if (isDescendant(source1, source2)) {
+ transitionsToRemove.add(t2);
+ } else {
+ t1Preempted = true;
+ break;
+ }
+ }
+ }
+ if (!t1Preempted) {
+ for (int t3 : transitionsToRemove) {
+ filteredTransitions.remove(t3);
+ }
+ filteredTransitions.add(t1);
+ }
+ }
+ *enabledTransitions = filteredTransitions;
+}
+
+void QScxmlStateMachinePrivate::getProperAncestors(std::vector<int> *ancestors, int state1,
+ int state2) const
+{
+ Q_ASSERT(ancestors);
+
+ if (state1 == -1) {
+ return;
+ }
+
+ int parent = state1;
+ do {
+ parent = m_stateTable->state(parent).parent;
+ if (parent == state2) {
+ break;
+ }
+ ancestors->push_back(parent);
+ } while (parent != -1);
+}
+
+void QScxmlStateMachinePrivate::microstep(const OrderedSet &enabledTransitions)
+{
+ if (qscxmlLog().isDebugEnabled()) {
+ qCDebug(qscxmlLog) << q_func()
+ << "starting microstep, configuration:"
+ << stateNames(m_configuration.list());
+ qCDebug(qscxmlLog) << q_func() << "enabled transitions:";
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ QString from = QStringLiteral("(none)");
+ if (transition.source != StateTable::InvalidIndex)
+ from = m_tableData->string(m_stateTable->state(transition.source).name);
+ QStringList to;
+ if (transition.targets == StateTable::InvalidIndex) {
+ to.append(QStringLiteral("(none)"));
+ } else {
+ for (int t : m_stateTable->array(transition.targets))
+ to.append(m_tableData->string(m_stateTable->state(t).name));
+ }
+ qCDebug(qscxmlLog) << q_func() << "\t" << t << ":" << from << "->"
+ << to.join(QLatin1Char(','));
+ }
+ }
+
+ exitStates(enabledTransitions);
+ executeTransitionContent(enabledTransitions);
+ enterStates(enabledTransitions);
+
+ qCDebug(qscxmlLog) << q_func() << "finished microstep, configuration:"
+ << stateNames(m_configuration.list());
+}
+
+void QScxmlStateMachinePrivate::exitStates(const OrderedSet &enabledTransitions)
+{
+ OrderedSet statesToExit;
+ computeExitSet(enabledTransitions, statesToExit);
+ auto statesToExitSorted = statesToExit.takeList();
+ std::sort(statesToExitSorted.begin(), statesToExitSorted.end(), std::greater<int>());
+ qCDebug(qscxmlLog) << q_func() << "exiting states" << stateNames(statesToExitSorted);
+ for (int s : statesToExitSorted) {
+ const auto &state = m_stateTable->state(s);
+ if (state.serviceFactoryIds != StateTable::InvalidIndex)
+ m_statesToInvoke.remove(s);
+ }
+ for (int s : statesToExitSorted) {
+ for (int h : historyStates(s)) {
+ const auto &hState = m_stateTable->state(h);
+ QVector<int> history;
+
+ for (int s0 : m_configuration) {
+ const auto &s0State = m_stateTable->state(s0);
+ if (hState.type == StateTable::State::DeepHistory) {
+ if (s0State.isAtomic() && isDescendant(s0, s))
+ history.append(s0);
+ } else {
+ if (s0State.parent == s)
+ history.append(s0);
+ }
+ }
+
+ m_historyValue[h] = history;
+ }
+ }
+ for (int s : statesToExitSorted) {
+ const auto &state = m_stateTable->state(s);
+ if (state.exitInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(state.exitInstructions);
+ m_configuration.remove(s);
+ emitStateActive(s, false);
+ removeService(s);
+ }
+
+ if (m_infoSignalProxy) {
+ emit m_infoSignalProxy->statesExited(
+ QVector<QScxmlStateMachineInfo::StateId>::fromStdVector(statesToExitSorted));
+ }
+}
+
+void QScxmlStateMachinePrivate::computeExitSet(const OrderedSet &enabledTransitions,
+ OrderedSet &statesToExit) const
+{
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ if (transition.targets == StateTable::InvalidIndex) {
+ // nothing to do here: there is no exit set
+ } else {
+ const int domain = getTransitionDomain(t);
+ for (int s : m_configuration) {
+ if (isDescendant(s, domain))
+ statesToExit.add(s);
+ }
+ }
+ }
+}
+
+void QScxmlStateMachinePrivate::executeTransitionContent(const OrderedSet &enabledTransitions)
+{
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ if (transition.transitionInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(transition.transitionInstructions);
+ }
+
+ if (m_infoSignalProxy) {
+ emit m_infoSignalProxy->transitionsTriggered(
+ QVector<QScxmlStateMachineInfo::TransitionId>::fromStdVector(
+ enabledTransitions.list()));
+ }
+}
+
+void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions)
+{
+ Q_Q(QScxmlStateMachine);
+
+ OrderedSet statesToEnter, statesForDefaultEntry;
+ HistoryContent defaultHistoryContent;
+ computeEntrySet(enabledTransitions, &statesToEnter, &statesForDefaultEntry,
+ &defaultHistoryContent);
+ auto sortedStates = statesToEnter.takeList();
+ std::sort(sortedStates.begin(), sortedStates.end());
+ qCDebug(qscxmlLog) << q_func() << "entering states" << stateNames(sortedStates);
+ for (int s : sortedStates) {
+ const auto &state = m_stateTable->state(s);
+ m_configuration.add(s);
+ if (state.serviceFactoryIds != StateTable::InvalidIndex)
+ m_statesToInvoke.insert(s);
+ if (m_stateTable->binding == StateTable::LateBinding && m_isFirstStateEntry[s]) {
+ if (state.initInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(state.initInstructions);
+ m_isFirstStateEntry[s] = false;
+ }
+ if (state.entryInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(state.entryInstructions);
+ if (statesForDefaultEntry.contains(s)) {
+ const auto &initialTransition = m_stateTable->transition(state.initialTransition);
+ if (initialTransition.transitionInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(initialTransition.transitionInstructions);
+ }
+ const int dhc = defaultHistoryContent.value(s);
+ if (dhc != StateTable::InvalidIndex)
+ m_executionEngine->execute(dhc);
+ if (state.type == StateTable::State::Final) {
+ if (state.parentIsScxmlElement()) {
+ m_runningState = Finished;
+ } else {
+ const auto &parent = m_stateTable->state(state.parent);
+ m_executionEngine->execute(state.doneData, m_tableData->string(parent.name));
+ if (parent.parent != StateTable::InvalidIndex) {
+ const auto &grandParent = m_stateTable->state(parent.parent);
+ if (grandParent.isParallel()) {
+ if (allInFinalStates(getChildStates(grandParent))) {
+ auto e = new QScxmlEvent;
+ e->setEventType(QScxmlEvent::InternalEvent);
+ e->setName(QStringLiteral("done.state.")
+ + m_tableData->string(grandParent.name));
+ q->submitEvent(e);
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int s : sortedStates)
+ emitStateActive(s, true);
+ if (m_infoSignalProxy) {
+ emit m_infoSignalProxy->statesEntered(
+ QVector<QScxmlStateMachineInfo::StateId>::fromStdVector(sortedStates));
+ }
+}
+
+void QScxmlStateMachinePrivate::computeEntrySet(const OrderedSet &enabledTransitions,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const
+{
+ Q_ASSERT(statesToEnter);
+ Q_ASSERT(statesForDefaultEntry);
+ Q_ASSERT(defaultHistoryContent);
+
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ if (transition.targets == StateTable::InvalidIndex)
+ // targetless transition, so nothing to do
+ continue;
+ for (int s : m_stateTable->array(transition.targets))
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ auto ancestor = getTransitionDomain(t);
+ OrderedSet targets;
+ getEffectiveTargetStates(&targets, t);
+ for (auto s : targets)
+ addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+}
+
+void QScxmlStateMachinePrivate::addDescendantStatesToEnter(
+ int stateIndex, OrderedSet *statesToEnter, OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const
+{
+ Q_ASSERT(statesToEnter);
+ Q_ASSERT(statesForDefaultEntry);
+ Q_ASSERT(defaultHistoryContent);
+
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.isHistoryState()) {
+ HistoryValues::const_iterator historyValueIter = m_historyValue.find(stateIndex);
+ if (historyValueIter != m_historyValue.end()) {
+ auto historyValue = historyValueIter.value();
+ for (int s : historyValue)
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ for (int s : historyValue)
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ } else {
+ const auto transitionIdx = m_stateTable->array(state.transitions)[0];
+ const auto &defaultHistoryTransition = m_stateTable->transition(transitionIdx);
+ defaultHistoryContent->operator[](state.parent) =
+ defaultHistoryTransition.transitionInstructions;
+ StateTable::Array targetStates = m_stateTable->array(defaultHistoryTransition.targets);
+ for (int s : targetStates)
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ for (int s : targetStates)
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+ } else {
+ statesToEnter->add(stateIndex);
+ if (state.isCompound()) {
+ statesForDefaultEntry->add(stateIndex);
+ if (state.initialTransition != StateTable::InvalidIndex) {
+ auto initialTransition = m_stateTable->transition(state.initialTransition);
+ auto initialTransitionTargets = m_stateTable->array(initialTransition.targets);
+ for (int targetStateIndex : initialTransitionTargets)
+ addDescendantStatesToEnter(targetStateIndex, statesToEnter,
+ statesForDefaultEntry, defaultHistoryContent);
+ for (int targetStateIndex : initialTransitionTargets)
+ addAncestorStatesToEnter(targetStateIndex, stateIndex, statesToEnter,
+ statesForDefaultEntry, defaultHistoryContent);
+ }
+ } else {
+ if (state.isParallel()) {
+ for (int child : getChildStates(state)) {
+ if (!hasDescendant(*statesToEnter, child))
+ addDescendantStatesToEnter(child, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+ }
+ }
+ }
+}
+
+void QScxmlStateMachinePrivate::addAncestorStatesToEnter(
+ int stateIndex, int ancestorIndex, OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry, HistoryContent *defaultHistoryContent) const
+{
+ Q_ASSERT(statesToEnter);
+ Q_ASSERT(statesForDefaultEntry);
+ Q_ASSERT(defaultHistoryContent);
+
+ std::vector<int> ancestors;
+ getProperAncestors(&ancestors, stateIndex, ancestorIndex);
+ for (int anc : ancestors) {
+ if (anc == -1) {
+ // we can't enter the state machine itself, so:
+ continue;
+ }
+ statesToEnter->add(anc);
+ const auto &ancState = m_stateTable->state(anc);
+ if (ancState.isParallel()) {
+ for (int child : getChildStates(ancState)) {
+ if (!hasDescendant(*statesToEnter, child))
+ addDescendantStatesToEnter(child, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+ }
+ }
+}
+
+std::vector<int> QScxmlStateMachinePrivate::getChildStates(
+ const QScxmlExecutableContent::StateTable::State &state) const
+{
+ std::vector<int> childStates;
+ auto kids = m_stateTable->array(state.childStates);
+ if (kids.isValid()) {
+ childStates.reserve(kids.size());
+ for (int kiddo : kids) {
+ switch (m_stateTable->state(kiddo).type) {
+ case StateTable::State::Normal:
+ case StateTable::State::Final:
+ case StateTable::State::Parallel:
+ childStates.push_back(kiddo);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return childStates;
+}
+
+bool QScxmlStateMachinePrivate::hasDescendant(const OrderedSet &statesToEnter, int childIdx) const
+{
+ for (int s : statesToEnter) {
+ if (isDescendant(s, childIdx))
+ return true;
+ }
+ return false;
+}
+
+bool QScxmlStateMachinePrivate::allDescendants(const OrderedSet &statesToEnter, int childdx) const
+{
+ for (int s : statesToEnter) {
+ if (!isDescendant(s, childdx))
+ return false;
+ }
+ return true;
+}
+
+bool QScxmlStateMachinePrivate::isDescendant(int state1, int state2) const
+{
+ int parent = state1;
+ do {
+ parent = m_stateTable->state(parent).parent;
+ if (parent == state2)
+ return true;
+ } while (parent != -1);
+ return false;
+}
+
+bool QScxmlStateMachinePrivate::allInFinalStates(const std::vector<int> &states) const
+{
+ if (states.empty())
+ return false;
+
+ for (int idx : states) {
+ if (!isInFinalState(idx))
+ return false;
+ }
+
+ return true;
+}
+
+bool QScxmlStateMachinePrivate::someInFinalStates(const std::vector<int> &states) const
+{
+ for (int stateIndex : states) {
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.type == StateTable::State::Final && m_configuration.contains(stateIndex))
+ return true;
+ }
+ return false;
+}
+
+bool QScxmlStateMachinePrivate::isInFinalState(int stateIndex) const
+{
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.isCompound())
+ return someInFinalStates(getChildStates(state)) && m_configuration.contains(stateIndex);
+ else if (state.isParallel())
+ return allInFinalStates(getChildStates(state));
+ else
+ return false;
+}
+
+int QScxmlStateMachinePrivate::getTransitionDomain(int transitionIndex) const
+{
+ const auto &transition = m_stateTable->transition(transitionIndex);
+ if (transition.source == -1)
+ //oooh, we have the initial transition of the state machine.
+ return -1;
+
+ OrderedSet tstates;
+ getEffectiveTargetStates(&tstates, transitionIndex);
+ if (tstates.isEmpty()) {
+ return StateTable::InvalidIndex;
+ } else {
+ const auto &sourceState = m_stateTable->state(transition.source);
+ if (transition.type == StateTable::Transition::Internal
+ && sourceState.isCompound()
+ && allDescendants(tstates, transition.source)) {
+ return transition.source;
+ } else {
+ tstates.add(transition.source);
+ return findLCCA(std::move(tstates));
+ }
+ }
+}
+
+int QScxmlStateMachinePrivate::findLCCA(OrderedSet &&states) const
+{
+ std::vector<int> ancestors;
+ const int head = *states.begin();
+ OrderedSet tail(std::move(states));
+ tail.removeHead();
+
+ getProperAncestors(&ancestors, head, StateTable::InvalidIndex);
+ for (int anc : ancestors) {
+ if (anc != -1) { // the state machine itself is always compound
+ const auto &ancState = m_stateTable->state(anc);
+ if (!ancState.isCompound())
+ continue;
+ }
+
+ if (allDescendants(tail, anc))
+ return anc;
+ }
+
+ return StateTable::InvalidIndex;
+}
+
+void QScxmlStateMachinePrivate::getEffectiveTargetStates(OrderedSet *targets,
+ int transitionIndex) const
+{
+ Q_ASSERT(targets);
+
+ const auto &transition = m_stateTable->transition(transitionIndex);
+ for (int s : m_stateTable->array(transition.targets)) {
+ const auto &state = m_stateTable->state(s);
+ if (state.isHistoryState()) {
+ HistoryValues::const_iterator historyValueIter = m_historyValue.find(s);
+ if (historyValueIter != m_historyValue.end()) {
+ for (int historyState : historyValueIter.value()) {
+ targets->add(historyState);
+ }
+ } else {
+ getEffectiveTargetStates(targets, m_stateTable->array(state.transitions)[0]);
+ }
+ } else {
+ targets->add(s);
+ }
+ }
+}
+
/*!
* Creates a state machine from the SCXML file specified by \a fileName.
*
@@ -403,7 +1401,7 @@ QScxmlStateMachine *QScxmlStateMachine::fromFile(const QString &fileName)
{
QFile scxmlFile(fileName);
if (!scxmlFile.open(QIODevice::ReadOnly)) {
- auto stateMachine = new QScxmlStateMachine;
+ auto stateMachine = new QScxmlStateMachine(&QScxmlStateMachine::staticMetaObject);
QScxmlError err(scxmlFile.fileName(), 0, 0, QStringLiteral("cannot open for reading"));
QScxmlStateMachinePrivate::get(stateMachine)->parserData()->m_errors.append(err);
return stateMachine;
@@ -426,35 +1424,29 @@ QScxmlStateMachine *QScxmlStateMachine::fromFile(const QString &fileName)
QScxmlStateMachine *QScxmlStateMachine::fromData(QIODevice *data, const QString &fileName)
{
QXmlStreamReader xmlReader(data);
- QScxmlParser parser(&xmlReader);
- parser.setFileName(fileName);
- parser.parse();
- auto stateMachine = parser.instantiateStateMachine();
- parser.instantiateDataModel(stateMachine);
- return stateMachine;
+ QScxmlCompiler compiler(&xmlReader);
+ compiler.setFileName(fileName);
+ return compiler.compile();
}
-/*!
- * Returns the list of parse errors that occurred while creating a state machine from an
- * SCXML file.
- */
QVector<QScxmlError> QScxmlStateMachine::parseErrors() const
{
Q_D(const QScxmlStateMachine);
return d->m_parserData ? d->m_parserData->m_errors : QVector<QScxmlError>();
}
-QScxmlStateMachine::QScxmlStateMachine(QObject *parent)
- : QObject(*new QScxmlStateMachinePrivate, parent)
+QScxmlStateMachine::QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent)
+ : QObject(*new QScxmlStateMachinePrivate(metaObject), parent)
{
Q_D(QScxmlStateMachine);
- d->init();
+ d->m_executionEngine = new QScxmlExecutionEngine(this);
}
QScxmlStateMachine::QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent)
: QObject(dd, parent)
{
- dd.init();
+ Q_D(QScxmlStateMachine);
+ d->m_executionEngine = new QScxmlExecutionEngine(this);
}
/*!
@@ -500,63 +1492,59 @@ QScxmlStateMachine::QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *p
*/
/*!
- \enum QScxmlStateMachine::BindingMethod
+ \property QScxmlStateMachine::sessionId
- This enum specifies the binding method. The binding method controls the point in time
- when the initial values are assigned to the data elements.
+ \brief The session ID of the current state machine.
- \value EarlyBinding All data elements are created and initialized at data-model initialization.
- This is the default.
- \value LateBinding All data elements are created at initialization, but the initial values are
- assigned only when the containing state is entered for the first time. This is done
- before any executable content is executed.
+ The session ID is used for message routing between parent and child state machines. If a state
+ machine is started by an \c <invoke> element, any event it sends will have the \c invokeid field
+ set to the session ID. The state machine will use the origin of an event (which is set by the
+ \e target or \e targetexpr attribute in a \c <send> element) to dispatch messages to the correct
+ child state machine.
+
+ \sa QScxmlEvent::invokeId()
*/
/*!
- * Returns the session ID for the current state machine.
- *
- * The session ID is used for message routing between parent and child state machines. If a state
- * machine is started by an \c <invoke> element, any event it sends will have the \c invokeid field
- * set to the session ID. The state machine will use the origin of an event (which is set by the
- * \e target or \e targetexpr attribute in a \c <send> element) to dispatch messages to the correct
- * child state machine.
- *
- * \sa setSessionId() QScxmlEvent::invokeId()
+ \property QScxmlStateMachine::name
+
+ \brief The name of the state machine as set by the \e name attribute of the \c <scxml> tag.
*/
-QString QScxmlStateMachine::sessionId() const
-{
- Q_D(const QScxmlStateMachine);
- return d->m_sessionId;
-}
+/*!
+ \property QScxmlStateMachine::invoked
+
+ \brief Whether the state machine was invoked from an outer state machine.
+
+ \c true when the state machine was started as a service with the \c <invoke> element,
+ \c false otherwise.
+ */
/*!
- Sets the session ID for the current state machine to \a id.
+ \property QScxmlStateMachine::parseErrors
- \sa sessionId()
+ \brief The list of parse errors that occurred while creating a state machine from an SCXML file.
*/
-void QScxmlStateMachine::setSessionId(const QString &id)
-{
- Q_D(QScxmlStateMachine);
- d->m_sessionId = id;
-}
/*!
- * Generates a unique ID by appending a unique number to the \a prefix.
- *
- * The number is only unique within a single run of an application. This method is used when an
- * invoked service does not have an ID set (the \e id attribute in \c <invoke>).
+ \property QScxmlStateMachine::loader
+
+ \brief The loader that is currently used to resolve and load URIs for the state machine.
*/
-QString QScxmlStateMachine::generateSessionId(const QString &prefix)
+
+QString QScxmlStateMachine::sessionId() const
+{
+ Q_D(const QScxmlStateMachine);
+
+ return d->m_sessionId;
+}
+
+QString QScxmlStateMachinePrivate::generateSessionId(const QString &prefix)
{
int id = ++QScxmlStateMachinePrivate::m_sessionIdCounter;
return prefix + QString::number(id);
}
-/*!
- * Returns \c true when the state machine was started as a service with the \c <invoke> element,
- * \c false otherwise.
- */
bool QScxmlStateMachine::isInvoked() const
{
Q_D(const QScxmlStateMachine);
@@ -597,25 +1585,21 @@ QScxmlDataModel *QScxmlStateMachine::dataModel() const
return d->m_dataModel;
}
-/*!
- * \internal
- * Sets the binding method to the specified value.
- */
-void QScxmlStateMachine::setDataBinding(QScxmlStateMachine::BindingMethod bindingMethod)
+void QScxmlStateMachine::setLoader(QScxmlCompiler::Loader *loader)
{
Q_D(QScxmlStateMachine);
- d->m_dataBinding = bindingMethod;
+ if (loader != d->m_loader) {
+ d->m_loader = loader;
+ emit loaderChanged(loader);
+ }
}
-/*!
- * Returns the binding method used by the state machine.
- */
-QScxmlStateMachine::BindingMethod QScxmlStateMachine::dataBinding() const
+QScxmlCompiler::Loader *QScxmlStateMachine::loader() const
{
Q_D(const QScxmlStateMachine);
- return d->m_dataBinding;
+ return d->m_loader;
}
/*!
@@ -645,271 +1629,23 @@ void QScxmlStateMachine::setTableData(QScxmlTableData *tableData)
Q_ASSERT(tableData);
d->m_tableData = tableData;
+ d->m_stateTable = reinterpret_cast<const QScxmlExecutableContent::StateTable *>(
+ tableData->stateMachineTable());
if (objectName().isEmpty()) {
setObjectName(tableData->name());
}
-}
-
-void QScxmlInternal::WrappedQStateMachine::beginSelectTransitions(QEvent *event)
-{
- Q_D(WrappedQStateMachine);
-
- if (event && event->type() == QScxmlEvent::scxmlEventType) {
- stateMachinePrivate()->m_event = *static_cast<QScxmlEvent *>(event);
- d->stateMachine()->dataModel()->setScxmlEvent(stateMachinePrivate()->m_event);
-
- auto scxmlEvent = static_cast<QScxmlEvent *>(event);
- auto smp = stateMachinePrivate();
-
- foreach (QScxmlInvokableService *service, smp->invokedServices()) {
- if (scxmlEvent->invokeId() == service->id()) {
- service->finalize();
- }
- if (service->autoforward()) {
- qCDebug(qscxmlLog) << this << "auto-forwarding event" << scxmlEvent->name()
- << "from" << stateMachine()->name() << "to service" << service->id();
- service->postEvent(new QScxmlEvent(*scxmlEvent));
- }
- }
-
- if (scxmlEvent->eventType() == QScxmlEvent::ExternalEvent) {
- emit d->stateMachine()->eventOccurred(*scxmlEvent);
- }
-
- if (scxmlEvent->originType() == QLatin1String("qt:signal")) {
- emit d->stateMachine()->externalEventOccurred(*scxmlEvent);
- }
-
- if (smp->m_eventFilter && !smp->m_eventFilter->handle(scxmlEvent, d->stateMachine())) {
- scxmlEvent->makeIgnorable();
- scxmlEvent->clear();
- smp->m_event.clear();
- return;
- }
- } else {
- stateMachinePrivate()->m_event.clear();
- d->stateMachine()->dataModel()->setScxmlEvent(stateMachinePrivate()->m_event);
- }
-}
-
-void QScxmlInternal::WrappedQStateMachine::beginMicrostep(QEvent *event)
-{
- Q_D(WrappedQStateMachine);
-
- qCDebug(qscxmlLog) << d->m_stateMachine
- << "started microstep from state" << d->m_stateMachine->activeStateNames()
- << "with event" << stateMachinePrivate()->m_event.name()
- << "and event type" << event->type();
-}
-
-void QScxmlInternal::WrappedQStateMachine::endMicrostep(QEvent *event)
-{
- Q_D(WrappedQStateMachine);
- Q_UNUSED(event);
-
- qCDebug(qscxmlLog) << d->m_stateMachine
- << "finished microstep in state (" << d->m_stateMachine->activeStateNames() << ")";
-}
-
-// This is a slightly modified copy of QStateMachinePrivate::event()
-// Instead of postExternalEvent and processEvents
-// we route event first to the appropriate state machine instance.
-bool QScxmlInternal::WrappedQStateMachine::event(QEvent *e)
-{
- Q_D(QScxmlInternal::WrappedQStateMachine);
- if (e->type() == QEvent::Timer) {
- QTimerEvent *te = static_cast<QTimerEvent*>(e);
- int tid = te->timerId();
- if (d->state != QStateMachinePrivate::Running) {
- // This event has been cancelled already
- QMutexLocker locker(&d->delayedEventsMutex);
- Q_ASSERT(!d->timerIdToDelayedEventId.contains(tid));
- return true;
- }
- d->delayedEventsMutex.lock();
- int id = d->timerIdToDelayedEventId.take(tid);
- QStateMachinePrivate::DelayedEvent ee = d->delayedEvents.take(id);
- if (ee.event != 0) {
- Q_ASSERT(ee.timerId == tid);
-// killTimer(tid);
-// d->delayedEventIdFreeList.release(id);
- d->delayedEventsMutex.unlock();
- d->_q_killDelayedEventTimer(id, tid);
- // route here
- if (ee.event->type() == QScxmlEvent::scxmlEventType)
- QScxmlStateMachinePrivate::get(stateMachine())->routeEvent(static_cast<QScxmlEvent *>(ee.event));
-// d->postExternalEvent(ee.event);
-// d->processEvents(QStateMachinePrivate::DirectProcessing);
- return true;
- } else {
- d->delayedEventsMutex.unlock();
- }
- }
- return QState::event(e);
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::noMicrostep()
-{
- qCDebug(qscxmlLog) << m_stateMachine
- << "had no transition, stays in state (" << m_stateMachine->activeStateNames() << ")";
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::processedPendingEvents(bool didChange)
-{
- qCDebug(qscxmlLog) << m_stateMachine << "finishedPendingEvents" << didChange << "in state ("
- << m_stateMachine->activeStateNames() << ")";
- emit m_stateMachine->reachedStableState();
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::beginMacrostep()
-{
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::endMacrostep(bool didChange)
-{
- qCDebug(qscxmlLog) << m_stateMachine << "endMacrostep" << didChange
- << "in state (" << m_stateMachine->activeStateNames() << ")";
-
- { // handle <invoke>s
- QVector<QScxmlState*> &sti = stateMachinePrivate()->m_statesToInvoke;
- std::sort(sti.begin(), sti.end(), WrappedQStateMachinePrivate::stateEntryLessThan);
- foreach (QScxmlState *s, sti) {
- auto sp = QScxmlStatePrivate::get(s);
- foreach (QScxmlInvokableService *s, sp->servicesWaitingToStart) {
- s->start();
- }
- sp->servicesWaitingToStart.clear();
- }
- sti.clear();
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::enterStates(
- QEvent *event,
- const QList<QAbstractState*> &exitedStates_sorted,
- const QList<QAbstractState*> &statesToEnter_sorted,
- const QSet<QAbstractState*> &statesForDefaultEntry,
- QHash<QAbstractState *, QVector<QPropertyAssignment> > &propertyAssignmentsForState
-# ifndef QT_NO_ANIMATION
- , const QList<QAbstractAnimation*> &selectedAnimations
-# endif
- )
-{
- QStateMachinePrivate::enterStates(event, exitedStates_sorted, statesToEnter_sorted,
- statesForDefaultEntry, propertyAssignmentsForState
-# ifndef QT_NO_ANIMATION
- , selectedAnimations
-# endif
- );
- foreach (QAbstractState *s, statesToEnter_sorted) {
- if (QScxmlState *qss = qobject_cast<QScxmlState *>(s)) {
- if (!QScxmlStatePrivate::get(qss)->invokableServiceFactories.isEmpty()) {
- if (!stateMachinePrivate()->m_statesToInvoke.contains(qss)) {
- stateMachinePrivate()->m_statesToInvoke.append(qss);
- }
- }
- }
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::exitStates(
- QEvent *event,
- const QList<QAbstractState *> &statesToExit_sorted,
- const QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates)
-{
- QStateMachinePrivate::exitStates(event, statesToExit_sorted, assignmentsForEnteredStates);
-
- auto smp = stateMachinePrivate();
- for (int i = 0; i < smp->m_statesToInvoke.size(); ) {
- if (statesToExit_sorted.contains(smp->m_statesToInvoke.at(i))) {
- smp->m_statesToInvoke.removeAt(i);
- } else {
- ++i;
- }
- }
-
- foreach (QAbstractState *s, statesToExit_sorted) {
- if (QScxmlState *qss = qobject_cast<QScxmlState *>(s)) {
- auto ssp = QScxmlStatePrivate::get(qss);
- ssp->servicesWaitingToStart.clear();
- QVector<QScxmlInvokableService *> &services = ssp->invokedServices;
- foreach (QScxmlInvokableService *service, services) {
- qCDebug(qscxmlLog) << stateMachine() << "schedule service cancellation" << service->id();
- QMetaObject::invokeMethod(q_func(),
- "removeAndDestroyService",
- Qt::QueuedConnection,
- Q_ARG(QScxmlInvokableService *,service));
- }
- services.clear();
- }
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::exitInterpreter()
-{
- Q_Q(WrappedQStateMachine);
-
- foreach (QAbstractState *s, configuration) {
- QScxmlExecutableContent::ContainerId onExitInstructions = QScxmlExecutableContent::NoInstruction;
- if (QScxmlFinalState *finalState = qobject_cast<QScxmlFinalState *>(s)) {
- stateMachinePrivate()->m_executionEngine->execute(finalState->doneData(), QVariant());
- onExitInstructions = QScxmlFinalStatePrivate::get(finalState)->onExitInstructions;
- } else if (QScxmlState *state = qobject_cast<QScxmlState *>(s)) {
- onExitInstructions = QScxmlStatePrivate::get(state)->onExitInstructions;
- }
-
- if (onExitInstructions != QScxmlExecutableContent::NoInstruction) {
- stateMachinePrivate()->m_executionEngine->execute(onExitInstructions);
- }
-
- if (QScxmlFinalState *finalState = qobject_cast<QScxmlFinalState *>(s)) {
- if (finalState->parent() == q) {
- if (auto psm = stateMachinePrivate()->m_parentStateMachine) {
- auto done = new QScxmlEvent;
- done->setName(QStringLiteral("done.invoke.") + m_stateMachine->sessionId());
- done->setInvokeId(m_stateMachine->sessionId());
- qCDebug(qscxmlLog) << "submitting event" << done->name() << "to" << psm->name();
- psm->submitEvent(done);
- }
- }
- }
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::emitStateFinished(QState *forState, QFinalState *guiltyState)
-{
- Q_Q(WrappedQStateMachine);
-
- if (QScxmlFinalState *finalState = qobject_cast<QScxmlFinalState *>(guiltyState)) {
- if (!q->isRunning())
- return;
- stateMachinePrivate()->m_executionEngine->execute(finalState->doneData(), forState->objectName());
- }
-
- QStateMachinePrivate::emitStateFinished(forState, guiltyState);
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::startupHook()
-{
- Q_Q(WrappedQStateMachine);
-
- q->submitQueuedEvents();
-}
-
-int QScxmlInternal::WrappedQStateMachinePrivate::eventIdForDelayedEvent(const QString &sendId)
-{
- QMutexLocker locker(&delayedEventsMutex);
-
- QHash<int, DelayedEvent>::const_iterator it;
- for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
- if (QScxmlEvent *e = dynamic_cast<QScxmlEvent *>(it->event)) {
- if (e->sendId() == sendId) {
- return it.key();
- }
- }
+ 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);
}
- return -1;
+ 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);
}
/*!
@@ -920,26 +1656,21 @@ int QScxmlInternal::WrappedQStateMachinePrivate::eventIdForDelayedEvent(const QS
* When it is \c false, the full list of all states will be returned.
*
* The returned list does not contain the states of possible nested state machines.
+ *
+ * \note The order of the state names in the list is the order in which the states occurred in
+ * the SCXML document.
*/
QStringList QScxmlStateMachine::stateNames(bool compress) const
{
Q_D(const QScxmlStateMachine);
- QList<QObject *> worklist;
- worklist.reserve(d->m_qStateMachine->children().size() + d->m_qStateMachine->configuration().size());
- worklist.append(d->m_qStateMachine->children());
-
- QStringList res;
- while (!worklist.isEmpty()) {
- QObject *obj = worklist.takeLast();
- if (QAbstractState *state = qobject_cast<QAbstractState *>(obj)) {
- if (!compress || !obj->children().count())
- res.append(state->objectName());
- worklist.append(obj->children());
- }
+ QStringList names;
+ for (int i = 0; i < d->m_stateTable->stateCount; ++i) {
+ const auto &state = d->m_stateTable->state(i);
+ if (!compress || state.isAtomic())
+ names.append(d->m_tableData->string(state.name));
}
- std::sort(res.begin(), res.end());
- return res;
+ return names;
}
/*!
@@ -953,19 +1684,13 @@ QStringList QScxmlStateMachine::activeStateNames(bool compress) const
{
Q_D(const QScxmlStateMachine);
- QSet<QAbstractState *> config = QStateMachinePrivate::get(d->m_qStateMachine)->configuration;
- if (compress)
- foreach (const QAbstractState *s, config)
- config.remove(s->parentState());
- QStringList res;
- foreach (const QAbstractState *s, config) {
- QString id = s->objectName();
- if (!id.isEmpty()) {
- res.append(id);
- }
+ QStringList result;
+ for (int stateIdx : d->m_configuration) {
+ const auto &state = d->m_stateTable->state(stateIdx);
+ if (state.isAtomic() || !compress)
+ result.append(d->m_tableData->string(state.name));
}
- std::sort(res.begin(), res.end());
- return res;
+ return result;
}
/*!
@@ -974,48 +1699,89 @@ QStringList QScxmlStateMachine::activeStateNames(bool compress) const
bool QScxmlStateMachine::isActive(const QString &scxmlStateName) const
{
Q_D(const QScxmlStateMachine);
- QSet<QAbstractState *> config = QStateMachinePrivate::get(d->m_qStateMachine)->configuration;
- foreach (QAbstractState *s, config) {
- if (s->objectName() == scxmlStateName) {
+
+ for (int stateIndex : d->m_configuration) {
+ const auto &state = d->m_stateTable->state(stateIndex);
+ if (d->m_tableData->string(state.name) == scxmlStateName)
return true;
- }
}
+
return false;
}
-/*!
- * Creates a connection of the given \a type from the state identified by \a scxmlStateName
- * to the \a method in the \a receiver object. The receiver's \a method
- * may contain a boolean argument that indicates whether the state connected
- * became active or inactive.
- *
- * Returns a handle to the connection, which can be used later to disconnect.
- */
-QMetaObject::Connection QScxmlStateMachine::connectToState(const QString &scxmlStateName,
- const QObject *receiver, const char *method,
- Qt::ConnectionType type)
+QMetaObject::Connection QScxmlStateMachine::connectToStateImpl(const QString &scxmlStateName,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type)
{
+ const int *types = Q_NULLPTR;
+ if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
+ types = QtPrivate::ConnectionTypes<QtPrivate::List<bool> >::types();
+
Q_D(QScxmlStateMachine);
- QAbstractState *state = findState(scxmlStateName, d->m_qStateMachine);
- return QObject::connect(state, SIGNAL(activeChanged(bool)), receiver, method, type);
+ int signalIndex = QScxmlInternal::signalIndex(d->m_metaObject,
+ scxmlStateName.toUtf8() + "Changed(bool)");
+ return signalIndex < 0 ? QMetaObject::Connection()
+ : QObjectPrivate::connectImpl(this, signalIndex, receiver, slot, slotObj,
+ type, types, d->m_metaObject);
}
/*!
- * Returns the SCXML event filter if one is set, otherwise returns null.
+ Creates a connection of the given \a type from the state identified by \a scxmlStateName
+ to the \a method in the \a receiver object. The receiver's \a method
+ may take a boolean argument that indicates whether the state connected
+ became active or inactive. For example:
+
+ \code
+ void mySlot(bool active);
+ \endcode
+
+ Returns a handle to the connection, which can be used later to disconnect.
*/
-QScxmlEventFilter *QScxmlStateMachine::scxmlEventFilter() const
+QMetaObject::Connection QScxmlStateMachine::connectToState(const QString &scxmlStateName,
+ const QObject *receiver,
+ const char *method,
+ Qt::ConnectionType type)
{
- Q_D(const QScxmlStateMachine);
- return d->m_eventFilter;
+ QByteArray signalName = QByteArray::number(QSIGNAL_CODE) + scxmlStateName.toUtf8()
+ + "Changed(bool)";
+ return QObject::connect(this, signalName.constData(), receiver, method, type);
}
/*!
- * Sets the \a newFilter as the SCXML event filter. Passing null will remove the current filter.
- */
-void QScxmlStateMachine::setScxmlEventFilter(QScxmlEventFilter *newFilter)
+ Creates a connection of the specified \a type from the event specified by
+ \a scxmlEventSpec to the \a method in the \a receiver object. The receiver's
+ \a method may take a QScxmlEvent as a parameter. For example:
+
+ \code
+ void mySlot(const QScxmlEvent &event);
+ \endcode
+
+ In contrast to event specifications in SCXML documents, spaces are not
+ allowed in the \a scxmlEventSpec here. In order to connect to multiple
+ events with different prefixes, connectToEvent() has to be called multiple
+ times.
+
+ Returns a handle to the connection, which can be used later to disconnect.
+*/
+QMetaObject::Connection QScxmlStateMachine::connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver,
+ const char *method,
+ Qt::ConnectionType type)
+{
+ Q_D(QScxmlStateMachine);
+ return d->m_router.connectToEvent(scxmlEventSpec.split(QLatin1Char('.')), receiver, method,
+ type);
+}
+
+QMetaObject::Connection QScxmlStateMachine::connectToEventImpl(const QString &scxmlEventSpec,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type)
{
Q_D(QScxmlStateMachine);
- d->m_eventFilter = newFilter;
+ return d->m_router.connectToEvent(scxmlEventSpec.split(QLatin1Char('.')), receiver, slot,
+ slotObj, type);
}
/*!
@@ -1058,7 +1824,7 @@ bool QScxmlStateMachine::isRunning() const
{
Q_D(const QScxmlStateMachine);
- return d->m_qStateMachine->isRunning();
+ return d->isRunnable() && !d->isPaused();
}
/*!
@@ -1089,9 +1855,6 @@ void QScxmlStateMachine::setInitialValues(const QVariantMap &initialValues)
}
}
-/*!
- * Returns the name of the state machine as set by the \e name attribute of the \c <scxml> tag.
- */
QString QScxmlStateMachine::name() const
{
return tableData()->name();
@@ -1116,9 +1879,7 @@ void QScxmlStateMachine::submitEvent(QScxmlEvent *event)
<< QScxmlEventPrivate::debugString(event).constData();
Q_ASSERT(event->eventType() == QScxmlEvent::ExternalEvent);
- int id = d->m_qStateMachine->postDelayedEvent(event, event->delay());
-
- qCDebug(qscxmlLog) << this << ": delayed event" << event->name() << "(" << event << ") got id:" << id;
+ d->submitDelayedEvent(event);
} else {
qCDebug(qscxmlLog) << this << "submitting event" << event->name()
<< ":" << QScxmlEventPrivate::debugString(event).constData();
@@ -1146,9 +1907,8 @@ void QScxmlStateMachine::submitEvent(const QString &eventName)
void QScxmlStateMachine::submitEvent(const QString &eventName, const QVariant &data)
{
QVariant incomingData = data;
- if (incomingData.canConvert<QJSValue>()) {
+ if (incomingData.canConvert<QJSValue>())
incomingData = incomingData.value<QJSValue>().toVariant();
- }
QScxmlEvent *e = new QScxmlEvent;
e->setName(eventName);
@@ -1164,49 +1924,16 @@ void QScxmlStateMachine::cancelDelayedEvent(const QString &sendId)
{
Q_D(QScxmlStateMachine);
- int id = d->m_qStateMachine->eventIdForDelayedEvent(sendId);
-
- qCDebug(qscxmlLog) << this << "canceling event" << sendId << "with id" << id;
-
- if (id != -1)
- d->m_qStateMachine->cancelDelayedEvent(id);
-}
-
-void QScxmlInternal::WrappedQStateMachine::queueEvent(QScxmlEvent *event, EventPriority priority)
-{
- Q_D(WrappedQStateMachine);
-
- if (!d->m_queuedEvents)
- d->m_queuedEvents = new QVector<WrappedQStateMachinePrivate::QueuedEvent>();
- d->m_queuedEvents->append(WrappedQStateMachinePrivate::QueuedEvent(event, priority));
-}
-
-void QScxmlInternal::WrappedQStateMachine::submitQueuedEvents()
-{
- Q_D(WrappedQStateMachine);
-
- qCDebug(qscxmlLog) << d->m_stateMachine << ": submitting queued events";
-
- if (d->m_queuedEvents) {
- foreach (const WrappedQStateMachinePrivate::QueuedEvent &e, *d->m_queuedEvents)
- postEvent(e.event, e.priority);
- delete d->m_queuedEvents;
- d->m_queuedEvents = Q_NULLPTR;
- }
-}
-
-int QScxmlInternal::WrappedQStateMachine::eventIdForDelayedEvent(const QString &sendId)
-{
- Q_D(WrappedQStateMachine);
- return d->eventIdForDelayedEvent(sendId);
-}
-
-void QScxmlInternal::WrappedQStateMachine::removeAndDestroyService(QScxmlInvokableService *service)
-{
- Q_D(WrappedQStateMachine);
- qCDebug(qscxmlLog) << stateMachine() << "canceling service" << service->id();
- if (d->stateMachinePrivate()->removeService(service)) {
- delete service;
+ for (auto it = d->m_delayedEvents.begin(), eit = d->m_delayedEvents.end(); it != eit; ++it) {
+ if (it->second->sendId() == sendId) {
+ qCDebug(qscxmlLog) << this
+ << "canceling event" << sendId
+ << "with timer id" << it->first;
+ d->m_eventLoopHook.killTimer(it->first);
+ delete it->second;
+ d->m_delayedEvents.erase(it);
+ return;
+ }
}
}
@@ -1235,10 +1962,9 @@ bool QScxmlStateMachine::isDispatchableTarget(const QString &target) const
if (target.startsWith(QStringLiteral("#_"))) {
QStringRef targetId = target.midRef(2);
- foreach (QScxmlInvokableService *service, d->m_invokedServices) {
- if (service->id() == targetId) {
+ for (auto invokedService : d->m_invokedServices) {
+ if (invokedService.service->id() == targetId)
return true;
- }
}
}
@@ -1246,6 +1972,24 @@ bool QScxmlStateMachine::isDispatchableTarget(const QString &target) const
}
/*!
+ \property QScxmlStateMachine::invokedServices
+ \brief A list of SCXML services that were invoked from the main
+ state machine (possibly recursively).
+*/
+
+QVector<QScxmlInvokableService *> QScxmlStateMachine::invokedServices() const
+{
+ Q_D(const QScxmlStateMachine);
+
+ QVector<QScxmlInvokableService *> result;
+ for (int i = 0, ei = int(d->m_invokedServices.size()); i != ei; ++i) {
+ if (auto service = d->m_invokedServices[size_t(i)].service)
+ result.append(service);
+ }
+ return result;
+}
+
+/*!
\fn QScxmlStateMachine::runningChanged(bool running)
This signal is emitted when the \c running property is changed with \a running as argument.
@@ -1297,7 +2041,8 @@ void QScxmlStateMachine::start()
if (!isInitialized() && !init())
qCDebug(qscxmlLog) << this << "cannot be initialized on start(). Starting anyway ...";
- d->m_qStateMachine->start();
+ d->start();
+ d->m_eventLoopHook.queueProcessEvents();
}
/*!
@@ -1309,16 +2054,13 @@ void QScxmlStateMachine::start()
void QScxmlStateMachine::stop()
{
Q_D(QScxmlStateMachine);
- d->m_qStateMachine->stop();
+ d->pause();
}
-/*!
- * \internal
- */
-void QScxmlStateMachine::setService(const QString &id, QScxmlInvokableService *service)
+bool QScxmlStateMachine::isActive(int stateIndex) const
{
- Q_UNUSED(id);
- Q_UNUSED(service);
+ Q_D(const QScxmlStateMachine);
+ return d->m_configuration.contains(stateIndex);
}
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h
index 5a800d5..2dea079 100644
--- a/src/scxml/qscxmlstatemachine.h
+++ b/src/scxml/qscxmlstatemachine.h
@@ -44,60 +44,50 @@
#include <QtScxml/qscxmlexecutablecontent.h>
#include <QtScxml/qscxmlerror.h>
#include <QtScxml/qscxmlevent.h>
+#include <QtScxml/qscxmlcompiler.h>
#include <QString>
#include <QVector>
#include <QUrl>
#include <QVariantList>
+#include <QPointer>
+
+#include <functional>
QT_BEGIN_NAMESPACE
class QIODevice;
class QXmlStreamWriter;
class QTextStream;
-class QScxmlEventBuilder;
-class QScxmlInvokableServiceFactory;
class QScxmlInvokableService;
-class QScxmlParser;
-class QScxmlStateMachine;
-class QScxmlTableData;
-
-class Q_SCXML_EXPORT QScxmlEventFilter
-{
-public:
- virtual ~QScxmlEventFilter();
- virtual bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) = 0;
-};
class QScxmlStateMachinePrivate;
class Q_SCXML_EXPORT QScxmlStateMachine: public QObject
{
Q_DECLARE_PRIVATE(QScxmlStateMachine)
Q_OBJECT
- Q_ENUMS(BindingMethod)
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
Q_PROPERTY(bool initialized READ isInitialized NOTIFY initializedChanged)
Q_PROPERTY(QScxmlDataModel *dataModel READ dataModel WRITE setDataModel NOTIFY dataModelChanged)
Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues NOTIFY initialValuesChanged)
+ Q_PROPERTY(QVector<QScxmlInvokableService *> invokedServices READ invokedServices NOTIFY invokedServicesChanged)
+ Q_PROPERTY(QString sessionId READ sessionId CONSTANT)
+ Q_PROPERTY(QString name READ name CONSTANT)
+ 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)
protected:
#ifndef Q_QDOC
- explicit QScxmlStateMachine(QObject *parent = nullptr);
- QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent);
+ explicit QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent = nullptr);
+ QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent = nullptr);
#endif // Q_QDOC
public:
- enum BindingMethod {
- EarlyBinding,
- LateBinding
- };
-
static QScxmlStateMachine *fromFile(const QString &fileName);
static QScxmlStateMachine *fromData(QIODevice *data, const QString &fileName = QString());
QVector<QScxmlError> parseErrors() const;
QString sessionId() const;
- void setSessionId(const QString &id);
- static QString generateSessionId(const QString &prefix);
bool isInvoked() const;
bool isInitialized() const;
@@ -105,7 +95,8 @@ public:
void setDataModel(QScxmlDataModel *model);
QScxmlDataModel *dataModel() const;
- BindingMethod dataBinding() const;
+ void setLoader(QScxmlCompiler::Loader *loader);
+ QScxmlCompiler::Loader *loader() const;
bool isRunning() const;
void setRunning(bool running);
@@ -114,34 +105,225 @@ public:
void setInitialValues(const QVariantMap &initialValues);
QString name() const;
- QStringList stateNames(bool compress = true) const;
- QStringList activeStateNames(bool compress = true) const;
- bool isActive(const QString &scxmlStateName) const;
+ Q_INVOKABLE QStringList stateNames(bool compress = true) const;
+ Q_INVOKABLE QStringList activeStateNames(bool compress = true) const;
+ Q_INVOKABLE bool isActive(const QString &scxmlStateName) const;
QMetaObject::Connection connectToState(const QString &scxmlStateName,
- const QObject *receiver, const char *method,
- Qt::ConnectionType type = Qt::AutoConnection);
+ const QObject *receiver, const char *method,
+ Qt::ConnectionType type = Qt::AutoConnection);
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ QMetaObject::Connection connectToState(const QString &scxmlStateName,
+ const QObject *receiver, PointerToMemberFunction method,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ template<typename Functor>
+ QMetaObject::Connection connectToState(const QString &scxmlStateName, Functor functor,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ template<typename Functor>
+ QMetaObject::Connection connectToState(const QString &scxmlStateName,
+ const QObject *context, Functor functor,
+ Qt::ConnectionType type = Qt::AutoConnection);
+#else
+
+ // connect state to a QObject slot
+ template <typename Func1>
+ inline QMetaObject::Connection connectToState(
+ const QString &scxmlStateName,
+ const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ typedef QtPrivate::FunctionPointer<Func1> SlotType;
+ return connectToStateImpl(
+ scxmlStateName, receiver, nullptr,
+ new QtPrivate::QSlotObject<Func1, typename SlotType::Arguments, void>(slot),
+ type);
+ }
+
+ // connect state to a functor or function pointer (without context)
+ template <typename Func1>
+ inline typename QtPrivate::QEnableIf<
+ !QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
+ !std::is_same<const char*, Func1>::value, QMetaObject::Connection>::Type
+ connectToState(const QString &scxmlStateName, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ // Use this as context
+ return connectToState(scxmlStateName, this, slot, type);
+ }
+
+ // connectToState to a functor or function pointer (with context)
+ template <typename Func1>
+ inline typename QtPrivate::QEnableIf<
+ !QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
+ !std::is_same<const char*, Func1>::value, QMetaObject::Connection>::Type
+ connectToState(const QString &scxmlStateName, QObject *context, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ QtPrivate::QSlotObjectBase *slotObj = new QtPrivate::QFunctorSlotObject<Func1, 1,
+ QtPrivate::List<bool>, void>(slot);
+ return connectToStateImpl(scxmlStateName, context, reinterpret_cast<void **>(&slot),
+ slotObj, type);
+ }
+#endif
+
+#ifdef Q_QDOC
+ static std::function<void(bool)> onEntry(const QObject *receiver, const char *method);
+ static std::function<void(bool)> onExit(const QObject *receiver, const char *method);
+
+ template<typename Functor>
+ static std::function<void(bool)> onEntry(Functor functor);
+
+ template<typename Functor>
+ static std::function<void(bool)> onExit(Functor functor);
- QScxmlEventFilter *scxmlEventFilter() const;
- void setScxmlEventFilter(QScxmlEventFilter *newFilter);
+ template<typename PointerToMemberFunction>
+ static std::function<void(bool)> onEntry(const QObject *receiver,
+ PointerToMemberFunction method);
+
+ template<typename PointerToMemberFunction>
+ static std::function<void(bool)> onExit(const QObject *receiver,
+ PointerToMemberFunction method);
+#else
+ static std::function<void(bool)> onEntry(const QObject *receiver, const char *method)
+ {
+ const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver));
+ return [receiverPointer, method](bool isEnteringState) {
+ if (isEnteringState && !receiverPointer.isNull())
+ QMetaObject::invokeMethod(const_cast<QObject *>(receiverPointer.data()), method);
+ };
+ }
+
+ static std::function<void(bool)> onExit(const QObject *receiver, const char *method)
+ {
+ const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver));
+ return [receiverPointer, method](bool isEnteringState) {
+ if (!isEnteringState && !receiverPointer.isNull())
+ QMetaObject::invokeMethod(receiverPointer.data(), method);
+ };
+ }
+
+ template<typename Functor>
+ static std::function<void(bool)> onEntry(Functor functor)
+ {
+ return [functor](bool isEnteringState) {
+ if (isEnteringState)
+ functor();
+ };
+ }
+
+ template<typename Functor>
+ static std::function<void(bool)> onExit(Functor functor)
+ {
+ return [functor](bool isEnteringState) {
+ if (!isEnteringState)
+ functor();
+ };
+ }
+
+ template<typename Func1>
+ static std::function<void(bool)> onEntry(
+ const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot)
+ {
+ typedef typename QtPrivate::FunctionPointer<Func1>::Object Object;
+ const QPointer<Object> receiverPointer(const_cast<Object *>(receiver));
+ return [receiverPointer, slot](bool isEnteringState) {
+ if (isEnteringState && !receiverPointer.isNull())
+ (receiverPointer->*slot)();
+ };
+ }
+
+ template<typename Func1>
+ static std::function<void(bool)> onExit(
+ const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot)
+ {
+ typedef typename QtPrivate::FunctionPointer<Func1>::Object Object;
+ const QPointer<Object> receiverPointer(const_cast<Object *>(receiver));
+ return [receiverPointer, slot](bool isEnteringState) {
+ if (!isEnteringState && !receiverPointer.isNull())
+ (receiverPointer->*slot)();
+ };
+ }
+#endif // !Q_QDOC
+
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver, const char *method,
+ Qt::ConnectionType type = Qt::AutoConnection);
+
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver, PointerToMemberFunction method,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ template<typename Functor>
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec, Functor functor,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ template<typename Functor>
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
+ const QObject *context, Functor functor,
+ Qt::ConnectionType type = Qt::AutoConnection);
+#else
+
+ // connect state to a QObject slot
+ template <typename Func1>
+ inline QMetaObject::Connection connectToEvent(
+ const QString &scxmlEventSpec,
+ const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ typedef QtPrivate::FunctionPointer<Func1> SlotType;
+ return connectToEventImpl(
+ scxmlEventSpec, receiver, nullptr,
+ new QtPrivate::QSlotObject<Func1, typename SlotType::Arguments, void>(slot),
+ type);
+ }
+
+ // connect state to a functor or function pointer (without context)
+ template <typename Func1>
+ inline typename QtPrivate::QEnableIf<
+ !QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
+ !std::is_same<const char*, Func1>::value, QMetaObject::Connection>::Type
+ connectToEvent(const QString &scxmlEventSpec, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ // Use this as context
+ return connectToEvent(scxmlEventSpec, this, slot, type);
+ }
+
+ // connectToEvent to a functor or function pointer (with context)
+ template <typename Func1>
+ inline typename QtPrivate::QEnableIf<
+ !QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
+ !std::is_same<const char*, Func1>::value, QMetaObject::Connection>::Type
+ connectToEvent(const QString &scxmlEventSpec, QObject *context, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ QtPrivate::QSlotObjectBase *slotObj = new QtPrivate::QFunctorSlotObject<Func1, 1,
+ QtPrivate::List<QScxmlEvent>, void>(slot);
+ return connectToEventImpl(scxmlEventSpec, context, reinterpret_cast<void **>(&slot),
+ slotObj, type);
+ }
+#endif
Q_INVOKABLE void submitEvent(QScxmlEvent *event);
Q_INVOKABLE void submitEvent(const QString &eventName);
Q_INVOKABLE void submitEvent(const QString &eventName, const QVariant &data);
- void cancelDelayedEvent(const QString &sendId);
+ Q_INVOKABLE void cancelDelayedEvent(const QString &sendId);
- bool isDispatchableTarget(const QString &target) const;
+ Q_INVOKABLE bool isDispatchableTarget(const QString &target) const;
+
+ QVector<QScxmlInvokableService *> invokedServices() const;
Q_SIGNALS:
void runningChanged(bool running);
+ void invokedServicesChanged(const QVector<QScxmlInvokableService *> &invokedServices);
void log(const QString &label, const QString &msg);
void reachedStableState();
void finished();
- void eventOccurred(const QScxmlEvent &event);
void dataModelChanged(QScxmlDataModel *model);
void initialValuesChanged(const QVariantMap &initialValues);
void initializedChanged(bool initialized);
- void externalEventOccurred(const QScxmlEvent &event);
+ void loaderChanged(QScxmlCompiler::Loader *loader);
public Q_SLOTS:
void start();
@@ -149,18 +331,27 @@ public Q_SLOTS:
bool init();
protected: // methods for friends:
- friend QScxmlDataModel;
- friend QScxmlEventBuilder;
- friend QScxmlInvokableServiceFactory;
- friend QScxmlExecutableContent::QScxmlExecutionEngine;
+ friend class QScxmlDataModel;
+ friend class QScxmlEventBuilder;
+ friend class QScxmlInvokableServicePrivate;
+ friend class QScxmlExecutionEngine;
#ifndef Q_QDOC
- void setDataBinding(BindingMethod bindingMethod);
- virtual void setService(const QString &id, QScxmlInvokableService *service);
-
+ // 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,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ QMetaObject::Connection connectToEventImpl(const QString &scxmlEventSpec,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type = Qt::AutoConnection);
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h
index 99334cf..69f3be4 100644
--- a/src/scxml/qscxmlstatemachine_p.h
+++ b/src/scxml/qscxmlstatemachine_p.h
@@ -53,44 +53,73 @@
#include <QtScxml/private/qscxmlexecutablecontent_p.h>
#include <QtScxml/qscxmlstatemachine.h>
-
-#include <QStateMachine>
-#include <QtCore/private/qstatemachine_p.h>
+#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/private/qmetaobject_p.h>
+#include <QtCore/qmetaobject.h>
QT_BEGIN_NAMESPACE
namespace QScxmlInternal {
-class WrappedQStateMachinePrivate;
-class WrappedQStateMachine: public QStateMachine
+class EventLoopHook: public QObject
{
Q_OBJECT
- Q_DECLARE_PRIVATE(WrappedQStateMachine)
-public:
- WrappedQStateMachine(QScxmlStateMachine *parent);
- WrappedQStateMachine(WrappedQStateMachinePrivate &dd, QScxmlStateMachine *parent);
+ QScxmlStateMachinePrivate *smp;
- QScxmlStateMachine *stateMachine() const;
+public:
+ EventLoopHook(QScxmlStateMachinePrivate *smp)
+ : smp(smp)
+ {}
- void queueEvent(QScxmlEvent *event, QStateMachine::EventPriority priority);
- void submitQueuedEvents();
- int eventIdForDelayedEvent(const QString &sendId);
+ void queueProcessEvents();
- Q_INVOKABLE void removeAndDestroyService(QScxmlInvokableService *service);
+ Q_INVOKABLE void doProcessEvents();
protected:
- void beginSelectTransitions(QEvent *event) Q_DECL_OVERRIDE;
- void beginMicrostep(QEvent *event) Q_DECL_OVERRIDE;
- void endMicrostep(QEvent *event) Q_DECL_OVERRIDE;
- bool event(QEvent *e) Q_DECL_OVERRIDE;
+ void timerEvent(QTimerEvent *timerEvent) Q_DECL_OVERRIDE;
+};
+
+class ScxmlEventRouter : public QObject
+{
+ Q_OBJECT
+public:
+ ScxmlEventRouter(QObject *parent = nullptr) : QObject(parent) {}
+ QMetaObject::Connection connectToEvent(const QStringList &segments, const QObject *receiver,
+ const char *method, Qt::ConnectionType type);
+ QMetaObject::Connection connectToEvent(const QStringList &segments, const QObject *receiver,
+ void **slot, QtPrivate::QSlotObjectBase *method,
+ Qt::ConnectionType type);
+
+ void route(const QStringList &segments, QScxmlEvent *event);
+
+signals:
+ void eventOccurred(const QScxmlEvent &event);
private:
- QScxmlStateMachinePrivate *stateMachinePrivate();
+ QHash<QString, ScxmlEventRouter *> children;
+ ScxmlEventRouter *child(const QString &segment);
+
+ void disconnectNotify(const QMetaMethod &signal) override;
};
-} // Internal namespace
+
+class StateMachineInfoProxy: public QObject
+{
+ Q_OBJECT
+
+public:
+ StateMachineInfoProxy(QObject *parent)
+ : QObject(parent)
+ {}
+
+Q_SIGNALS:
+ void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
+};
+} // QScxmlInternal namespace
class QScxmlInvokableService;
-class QScxmlState;
class QScxmlStateMachinePrivate: public QObjectPrivate
{
Q_DECLARE_PUBLIC(QScxmlStateMachine)
@@ -98,6 +127,27 @@ class QScxmlStateMachinePrivate: public QObjectPrivate
static QAtomicInt m_sessionIdCounter;
public: // types
+ typedef QScxmlExecutableContent::StateTable StateTable;
+
+ class HistoryContent
+ {
+ QHash<int, int> storage;
+
+ public:
+ HistoryContent() { storage.reserve(4); }
+
+ int &operator[](int idx) {
+ QHash<int, int>::Iterator i = storage.find(idx);
+ return (i == storage.end()) ? storage.insert(idx, StateTable::InvalidIndex).value() :
+ i.value();
+ }
+
+ int value(int idx) const {
+ QHash<int, int>::ConstIterator i = storage.constFind(idx);
+ return (i == storage.constEnd()) ? StateTable::InvalidIndex : i.value();
+ }
+ };
+
class ParserData
{
public:
@@ -105,55 +155,230 @@ public: // types
QVector<QScxmlError> m_errors;
};
+ // The OrderedSet is a set where it elements are in insertion order. See
+ // http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation under Algorithm, Datatypes. It
+ // is used to keep lists of states and transitions in document order.
+ class OrderedSet
+ {
+ std::vector<int> storage;
+
+ public:
+ OrderedSet(){}
+ OrderedSet(std::initializer_list<int> l): storage(l) {}
+
+ std::vector<int> takeList() const
+ { return std::move(storage); }
+
+ const std::vector<int> &list() const
+ { return storage; }
+
+ bool contains(int i) const
+ {
+ return std::find(storage.cbegin(), storage.cend(), i) != storage.cend();
+ }
+
+ bool remove(int i)
+ {
+ std::vector<int>::iterator it = std::find(storage.begin(), storage.end(), i);
+ if (it == storage.end()) {
+ return false;
+ }
+ storage.erase(it);
+ return true;
+ }
+
+ void removeHead()
+ { if (!isEmpty()) storage.erase(storage.begin()); }
+
+ bool isEmpty() const
+ { return storage.empty(); }
+
+ void add(int i)
+ { if (!contains(i)) storage.push_back(i); }
+
+ bool intersectsWith(const OrderedSet &other) const
+ {
+ for (auto i : storage) {
+ if (other.contains(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void clear()
+ { storage.clear(); }
+
+ typedef std::vector<int>::const_iterator const_iterator;
+ const_iterator begin() const { return storage.cbegin(); }
+ const_iterator end() const { return storage.cend(); }
+ };
+
+ class Queue
+ {
+ QVector<QScxmlEvent *> storage;
+
+ public:
+ Queue()
+ { storage.reserve(4); }
+
+ ~Queue()
+ { qDeleteAll(storage); }
+
+ void enqueue(QScxmlEvent *e)
+ { storage.append(e); }
+
+ bool isEmpty() const
+ { return storage.empty(); }
+
+ QScxmlEvent *dequeue()
+ {
+ Q_ASSERT(!isEmpty());
+ QScxmlEvent *e = storage.first();
+ storage.pop_front();
+ int sz = storage.size();
+ if (Q_UNLIKELY(sz > 4 && sz * 8 < storage.capacity())) {
+ storage.squeeze();
+ }
+ return e;
+ }
+ };
+
public:
- QScxmlStateMachinePrivate();
+ QScxmlStateMachinePrivate(const QMetaObject *qMetaObject);
~QScxmlStateMachinePrivate();
- void init();
-
static QScxmlStateMachinePrivate *get(QScxmlStateMachine *t)
{ return t->d_func(); }
- void setQStateMachine(QScxmlInternal::WrappedQStateMachine *stateMachine);
-
- QAbstractState *stateByScxmlName(const QString &scxmlName);
+ static QString generateSessionId(const QString &prefix);
ParserData *parserData();
void setIsInvoked(bool invoked)
{ m_isInvoked = invoked; }
- const QVector<QScxmlInvokableService *> &invokedServices() const
- { return m_invokedServices; }
-
- void addService(QScxmlInvokableService *service);
-
- bool removeService(QScxmlInvokableService *service);
+ void addService(int invokingState);
+ void removeService(int invokingState);
+ QScxmlInvokableServiceFactory *serviceFactory(int id);
bool executeInitialSetup();
void routeEvent(QScxmlEvent *event);
void postEvent(QScxmlEvent *event);
+ void submitDelayedEvent(QScxmlEvent *event);
void submitError(const QString &type, const QString &msg, const QString &sendid = QString());
+ void start();
+ void pause();
+ void processEvents();
+
+ void setEvent(QScxmlEvent *event);
+ void resetEvent();
+
+ void emitStateActive(int stateIndex, bool active);
+ void emitInvokedServicesChanged();
+ void emitSignalForEvent(int signalIndex, const QVariant &data);
+
+ void attach(QScxmlStateMachineInfo *info);
+ const OrderedSet &configuration() const { return m_configuration; }
+
+private:
+ QStringList stateNames(const std::vector<int> &stateIndexes) const;
+ std::vector<int> historyStates(int stateIdx) const;
+
+ void exitInterpreter();
+ void returnDoneEvent(QScxmlExecutableContent::ContainerId doneData);
+ bool nameMatch(const StateTable::Array &patterns, QScxmlEvent *event) const;
+ void selectTransitions(OrderedSet &enabledTransitions,
+ const std::vector<int> &configInDocumentOrder,
+ QScxmlEvent *event) const;
+ void removeConflictingTransitions(OrderedSet *enabledTransitions) const;
+ void getProperAncestors(std::vector<int> *ancestors, int state1, int state2) const;
+ void microstep(const OrderedSet &enabledTransitions);
+ void exitStates(const OrderedSet &enabledTransitions);
+ void computeExitSet(const OrderedSet &enabledTransitions, OrderedSet &statesToExit) const;
+ void executeTransitionContent(const OrderedSet &enabledTransitions);
+ void enterStates(const OrderedSet &enabledTransitions);
+ void computeEntrySet(const OrderedSet &enabledTransitions,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const;
+ void addDescendantStatesToEnter(int stateIndex,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const;
+ void addAncestorStatesToEnter(int stateIndex,
+ int ancestorIndex,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const;
+ std::vector<int> getChildStates(const StateTable::State &state) const;
+ bool hasDescendant(const OrderedSet &statesToEnter, int childIdx) const;
+ bool allDescendants(const OrderedSet &statesToEnter, int childdx) const;
+ bool isDescendant(int state1, int state2) const;
+ bool allInFinalStates(const std::vector<int> &states) const;
+ bool someInFinalStates(const std::vector<int> &states) const;
+ bool isInFinalState(int stateIndex) const;
+ int getTransitionDomain(int transitionIndex) const;
+ int findLCCA(OrderedSet &&states) const;
+ void getEffectiveTargetStates(OrderedSet *targets, int transitionIndex) const;
+
public: // types & data fields:
QString m_sessionId;
bool m_isInvoked;
bool m_isInitialized;
+ bool m_isProcessingEvents;
QVariantMap m_initialValues;
QScxmlDataModel *m_dataModel;
- QScxmlStateMachine::BindingMethod m_dataBinding;
- QScxmlExecutableContent::QScxmlExecutionEngine *m_executionEngine;
+ QScxmlCompilerPrivate::DefaultLoader m_defaultLoader;
+ QScxmlCompiler::Loader *m_loader;
+ QScxmlExecutionEngine *m_executionEngine;
QScxmlTableData *m_tableData;
- QScxmlEvent m_event;
- QScxmlInternal::WrappedQStateMachine *m_qStateMachine;
- QScxmlEventFilter *m_eventFilter;
- QVector<QScxmlState*> m_statesToInvoke;
+ const StateTable *m_stateTable;
QScxmlStateMachine *m_parentStateMachine;
+ QScxmlInternal::EventLoopHook m_eventLoopHook;
+ typedef std::vector<std::pair<int, QScxmlEvent *>> DelayedQueue;
+ DelayedQueue m_delayedEvents;
+ const QMetaObject *m_metaObject;
+ QScxmlInternal::ScxmlEventRouter m_router;
private:
- QVector<QScxmlInvokableService *> m_invokedServices;
QScopedPointer<ParserData> m_parserData; // used when created by StateMachine::fromFile.
+ typedef QHash<int, QVector<int>> HistoryValues;
+ struct InvokedService {
+ int invokingState;
+ QScxmlInvokableService *service;
+ QString serviceName;
+ };
+
+ // TODO: move the stuff below to a struct that can be reset
+ HistoryValues m_historyValue;
+ OrderedSet m_configuration;
+ Queue m_internalQueue;
+ Queue m_externalQueue;
+ QSet<int> m_statesToInvoke;
+ std::vector<InvokedService> m_invokedServices;
+ std::vector<bool> m_isFirstStateEntry;
+ std::vector<QScxmlInvokableServiceFactory *> m_cachedFactories;
+ enum { Invalid = 0, Starting, Running, Paused, Finished } m_runningState = Invalid;
+ bool isRunnable() const {
+ switch (m_runningState) {
+ case Starting:
+ case Running:
+ case Paused:
+ return true;
+ case Invalid:
+ case Finished:
+ return false;
+ }
+
+ return false; // Dead code, but many dumb compilers cannot (or are unwilling to) detect that.
+ }
+
+ bool isPaused() const { return m_runningState == Paused; }
+
+ QScxmlInternal::StateMachineInfoProxy *m_infoSignalProxy;
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachineinfo.cpp b/src/scxml/qscxmlstatemachineinfo.cpp
new file mode 100644
index 0000000..d81956a
--- /dev/null
+++ b/src/scxml/qscxmlstatemachineinfo.cpp
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscxmlstatemachineinfo_p.h"
+#include "qscxmlstatemachine_p.h"
+#include "qscxmlexecutablecontent_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlStateMachineInfoPrivate: public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QScxmlStateMachineInfo)
+
+public:
+ QScxmlStateMachine *stateMachine() const
+ { return qobject_cast<QScxmlStateMachine *>(q_func()->parent()); }
+
+ QScxmlStateMachinePrivate *stateMachinePrivate() const
+ { return QScxmlStateMachinePrivate::get(stateMachine()); }
+
+ const QScxmlExecutableContent::StateTable *stateTable() const
+ { return stateMachinePrivate()->m_stateTable; }
+};
+
+QScxmlStateMachineInfo::QScxmlStateMachineInfo(QScxmlStateMachine *stateMachine)
+ : QObject(*new QScxmlStateMachineInfoPrivate, stateMachine)
+{
+ QScxmlStateMachinePrivate::get(stateMachine)->attach(this);
+}
+
+QScxmlStateMachine *QScxmlStateMachineInfo::stateMachine() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ return d->stateMachine();
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::allStates() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::StateId> all;
+ for (int i = 0, ei = d->stateTable()->stateCount; i < ei; ++i) {
+ all.append(i);
+ }
+ return all;
+}
+
+QVector<QScxmlStateMachineInfo::TransitionId> QScxmlStateMachineInfo::allTransitions() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::TransitionId> all;
+ for (int i = 0, ei = d->stateTable()->transitionCount; i < ei; ++i) {
+ all.append(i);
+ }
+ return all;
+}
+
+QString QScxmlStateMachineInfo::stateName(int stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (stateId < 0 || stateId >= d->stateTable()->stateCount)
+ return QString();
+
+ auto state = d->stateTable()->state(stateId);
+ if (state.name >= 0)
+ return d->stateMachinePrivate()->m_tableData->string(state.name);
+ else
+ return QString();
+}
+
+QScxmlStateMachineInfo::StateId QScxmlStateMachineInfo::stateParent(StateId stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (stateId < 0 || stateId >= d->stateTable()->stateCount)
+ return InvalidStateId;
+
+ auto state = d->stateTable()->state(stateId);
+ return state.parent;
+}
+
+QScxmlStateMachineInfo::StateType QScxmlStateMachineInfo::stateType(StateId stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (stateId < 0 || stateId >= d->stateTable()->stateCount)
+ return InvalidState;
+
+ auto state = d->stateTable()->state(stateId);
+ switch (state.type) {
+ default: return InvalidState;
+ case QScxmlExecutableContent::StateTable::State::Normal: return NormalState;
+ case QScxmlExecutableContent::StateTable::State::Parallel: return ParallelState;
+ case QScxmlExecutableContent::StateTable::State::Final: return FinalState;
+ case QScxmlExecutableContent::StateTable::State::ShallowHistory: return ShallowHistoryState;
+ case QScxmlExecutableContent::StateTable::State::DeepHistory: return DeepHistoryState;
+ }
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::stateChildren(StateId stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ int childStates = QScxmlExecutableContent::StateTable::InvalidIndex;
+ if (stateId == InvalidStateId)
+ childStates = d->stateTable()->childStates;
+ if (stateId >= 0 && stateId < d->stateTable()->stateCount)
+ childStates = d->stateTable()->state(stateId).childStates;
+
+ QVector<QScxmlStateMachineInfo::StateId> all;
+ if (childStates == QScxmlExecutableContent::StateTable::InvalidIndex)
+ return all;
+
+ const auto kids = d->stateTable()->array(childStates);
+ all.reserve(kids.size());
+ for (auto childId : kids) {
+ all.append(childId);
+ }
+ return all;
+}
+
+QScxmlStateMachineInfo::TransitionType QScxmlStateMachineInfo::transitionType(QScxmlStateMachineInfo::TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return InvalidTransition;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ switch (transition.type) {
+ default: return InvalidTransition;
+ case QScxmlExecutableContent::StateTable::Transition::Invalid: return InvalidTransition;
+ case QScxmlExecutableContent::StateTable::Transition::Internal: return InternalTransition;
+ case QScxmlExecutableContent::StateTable::Transition::External: return ExternalTransition;
+ case QScxmlExecutableContent::StateTable::Transition::Synthetic: return SyntheticTransition;
+ }
+}
+
+QScxmlStateMachineInfo::TransitionId QScxmlStateMachineInfo::initialTransition(StateId stateId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (stateId == InvalidStateId)
+ return d->stateTable()->initialTransition;
+
+ if (stateId < 0 || stateId >= d->stateTable()->stateCount)
+ return InvalidTransitionId;
+
+ return d->stateTable()->state(stateId).initialTransition;
+}
+
+QScxmlStateMachineInfo::StateId QScxmlStateMachineInfo::transitionSource(TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return InvalidStateId;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ return transition.source;
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::transitionTargets(TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QScxmlStateMachineInfo::StateId> targets;
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return targets;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ if (transition.targets == QScxmlExecutableContent::StateTable::InvalidIndex)
+ return targets;
+
+ for (int target : d->stateTable()->array(transition.targets)) {
+ targets.append(target);
+ }
+
+ return targets;
+}
+
+QVector<QString> QScxmlStateMachineInfo::transitionEvents(TransitionId transitionId) const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ QVector<QString> events;
+ if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
+ return events;
+
+ auto transition = d->stateTable()->transition(transitionId);
+ if (transition.events == QScxmlExecutableContent::StateTable::InvalidIndex)
+ return events;
+
+ auto eventIds = d->stateTable()->array(transition.events);
+ events.reserve(eventIds.size());
+ for (auto eventId : eventIds) {
+ events.append(d->stateMachinePrivate()->m_tableData->string(eventId));
+ }
+
+ return events;
+}
+
+QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::configuration() const
+{
+ Q_D(const QScxmlStateMachineInfo);
+
+ return QVector<StateId>::fromStdVector(d->stateMachinePrivate()->configuration().list());
+}
+
+QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachineinfo_p.h b/src/scxml/qscxmlstatemachineinfo_p.h
new file mode 100644
index 0000000..7a8ca50
--- /dev/null
+++ b/src/scxml/qscxmlstatemachineinfo_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCXMLSTATEMACHINEINFO_H
+#define QSCXMLSTATEMACHINEINFO_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScxml/qscxmlglobals.h>
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlStateMachine;
+class QScxmlStateMachineInfoPrivate;
+
+class Q_SCXML_EXPORT QScxmlStateMachineInfo: public QObject
+{
+ Q_OBJECT
+
+public: // types
+ typedef int StateId;
+ typedef int TransitionId;
+
+ static const StateId InvalidStateId = -1;
+ static const TransitionId InvalidTransitionId = -1;
+
+ enum StateType : int {
+ InvalidState = -1,
+ NormalState = 0,
+ ParallelState = 1,
+ FinalState = 2,
+ ShallowHistoryState = 3,
+ DeepHistoryState = 4
+ };
+
+ enum TransitionType : int {
+ InvalidTransition = -1,
+ InternalTransition = 0,
+ ExternalTransition = 1,
+ SyntheticTransition = 2
+ };
+
+public: // methods
+ QScxmlStateMachineInfo(QScxmlStateMachine *stateMachine);
+
+ QScxmlStateMachine *stateMachine() const;
+
+ QVector<StateId> allStates() const;
+ QVector<TransitionId> allTransitions() const;
+ QString stateName(int stateId) const;
+ StateId stateParent(StateId stateId) const;
+ StateType stateType(int stateId) const;
+ QVector<StateId> stateChildren(StateId stateId) const;
+ TransitionId initialTransition(StateId stateId) const;
+ TransitionType transitionType(TransitionId transitionId) const;
+ StateId transitionSource(TransitionId transitionId) const;
+ QVector<StateId> transitionTargets(TransitionId transitionId) const;
+ QVector<QString> transitionEvents(TransitionId transitionId) const;
+ QVector<StateId> configuration() const;
+
+Q_SIGNALS:
+ void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
+ void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
+
+private:
+ Q_DECLARE_PRIVATE(QScxmlStateMachineInfo)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLSTATEMACHINEINFO_H
diff --git a/src/scxml/qscxmltabledata.cpp b/src/scxml/qscxmltabledata.cpp
index 75469f9..2457840 100644
--- a/src/scxml/qscxmltabledata.cpp
+++ b/src/scxml/qscxmltabledata.cpp
@@ -37,11 +37,1005 @@
**
****************************************************************************/
-#include "qscxmltabledata.h"
+#include "qscxmltabledata_p.h"
+#include "qscxmlcompiler_p.h"
+#include "qscxmlexecutablecontent_p.h"
-QT_BEGIN_NAMESPACE
+QT_USE_NAMESPACE
+
+using namespace QScxmlInternal;
+
+namespace {
+using namespace QScxmlExecutableContent;
+
+class TableDataBuilder: public DocumentModel::NodeVisitor
+{
+public:
+ TableDataBuilder(GeneratedTableData &tableData,
+ GeneratedTableData::MetaDataInfo &metaDataInfo,
+ GeneratedTableData::DataModelInfo &dataModelInfo,
+ GeneratedTableData::CreateFactoryId func)
+ : createFactoryId(func)
+ , m_tableData(tableData)
+ , m_dataModelInfo(dataModelInfo)
+ , m_stringTable(tableData.theStrings)
+ , m_instructions(tableData.theInstructions)
+ , m_evaluators(tableData.theEvaluators)
+ , m_assignments(tableData.theAssignments)
+ , m_foreaches(tableData.theForeaches)
+ , m_dataIds(tableData.theDataNameIds)
+ , m_stateNames(metaDataInfo.stateNames)
+
+ {
+ m_activeSequences.reserve(4);
+ tableData.theInitialSetup = QScxmlExecutableContent::NoInstruction;
+ }
+
+ void buildTableData(DocumentModel::ScxmlDocument *doc)
+ {
+ m_isCppDataModel = doc->root->dataModel == DocumentModel::Scxml::CppDataModel;
+ m_parents.reserve(32);
+ m_allTransitions.resize(doc->allTransitions.size());
+ m_docTransitionIndices.reserve(doc->allTransitions.size());
+ for (auto *t : qAsConst(doc->allTransitions)) {
+ m_docTransitionIndices.insert(t, m_docTransitionIndices.size());
+ }
+ m_docStatesIndices.reserve(doc->allStates.size());
+ m_transitionsForState.resize(doc->allStates.size());
+ m_allStates.resize(doc->allStates.size());
+ for (DocumentModel::AbstractState *s : qAsConst(doc->allStates)) {
+ m_docStatesIndices.insert(s, m_docStatesIndices.size());
+ }
+
+ doc->root->accept(this);
+ m_stateTable.version = Q_QSCXMLC_OUTPUT_REVISION;
+ generateStateMachineData();
+
+ m_tableData.theInstructions.squeeze();
+ }
+
+ void generateStateMachineData()
+ {
+ const int tableSize = sizeof(StateTable) / sizeof(qint32);
+ const int stateSize = qint32(sizeof(StateTable::State) / sizeof(qint32));
+ const int transitionSize = qint32(sizeof(StateTable::Transition) / sizeof(qint32));
+
+ m_stateTable.stateOffset = tableSize;
+ m_stateTable.stateCount = m_allStates.size();
+ m_stateTable.transitionOffset = m_stateTable.stateOffset +
+ m_stateTable.stateCount * stateSize;
+ m_stateTable.transitionCount = m_allTransitions.size();
+ m_stateTable.arrayOffset = m_stateTable.transitionOffset +
+ m_stateTable.transitionCount * transitionSize;
+ m_stateTable.arraySize = m_arrays.size();
+
+ const qint32 dataSize = qint32(tableSize)
+ + (m_allStates.size() * stateSize)
+ + (m_allTransitions.size() * transitionSize)
+ + m_arrays.size()
+ + 1;
+ QVector<qint32> data(dataSize, -1);
+ qint32 *ptr = data.data();
+
+ memcpy(ptr, &m_stateTable, sizeof(m_stateTable));
+ ptr += tableSize;
+
+ Q_ASSERT(ptr == data.constData() + m_stateTable.stateOffset);
+ memcpy(ptr, m_allStates.constData(),
+ sizeof(StateTable::State) * size_t(m_allStates.size()));
+ ptr += stateSize * size_t(m_allStates.size());
+
+ Q_ASSERT(ptr == data.constData() + m_stateTable.transitionOffset);
+ memcpy(ptr, m_allTransitions.constData(),
+ sizeof(StateTable::Transition) * size_t(m_allTransitions.size()));
+ ptr += transitionSize * size_t(m_allTransitions.size());
+
+ Q_ASSERT(ptr == data.constData() + m_stateTable.arrayOffset);
+ memcpy(ptr, m_arrays.constData(), sizeof(qint32) * size_t(m_arrays.size()));
+ ptr += m_arrays.size();
+
+ *ptr++ = StateTable::terminator;
+
+ Q_ASSERT(ptr == data.constData() + dataSize);
+
+ m_tableData.theStateMachineTable = data;
+ }
+
+protected: // visitor
+ using NodeVisitor::visit;
+
+ bool visit(DocumentModel::Scxml *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ setName(node->name);
+
+ switch (node->dataModel) {
+ case DocumentModel::Scxml::NullDataModel:
+ m_stateTable.dataModel = StateTable::NullDataModel;
+ break;
+ case DocumentModel::Scxml::JSDataModel:
+ m_stateTable.dataModel = StateTable::EcmaScriptDataModel;
+ break;
+ case DocumentModel::Scxml::CppDataModel:
+ m_stateTable.dataModel = StateTable::CppDataModel;
+ break;
+ default:
+ m_stateTable.dataModel = StateTable::InvalidDataModel;
+ break;
+ }
+
+ switch (node->binding) {
+ case DocumentModel::Scxml::EarlyBinding:
+ m_stateTable.binding = StateTable::EarlyBinding;
+ break;
+ case DocumentModel::Scxml::LateBinding:
+ m_stateTable.binding = StateTable::LateBinding;
+ m_bindLate = true;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ m_stateTable.name = addString(node->name);
+
+ m_parents.append(-1);
+ visit(node->children);
+
+ m_dataElements.append(node->dataElements);
+ if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) {
+ setInitialSetup(startNewSequence());
+ generate(m_dataElements);
+ if (node->script) {
+ node->script->accept(this);
+ }
+ visit(&node->initialSetup);
+ endSequence();
+ }
+
+ QVector<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *sot : qAsConst(node->children)) {
+ if (DocumentModel::AbstractState *s = sot->asAbstractState()) {
+ childStates.append(s);
+ }
+ }
+ m_stateTable.childStates = addStates(childStates);
+ if (node->initialTransition) {
+ visit(node->initialTransition);
+ const int transitionIndex = m_docTransitionIndices.value(node->initialTransition, -1);
+ Q_ASSERT(transitionIndex != -1);
+ m_stateTable.initialTransition = transitionIndex;
+ }
+ m_parents.removeLast();
+
+ return false;
+ }
+
+ bool visit(DocumentModel::State *state) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ m_stateNames.add(state->id);
+ const int stateIndex = m_docStatesIndices.value(state, -1);
+ Q_ASSERT(stateIndex != -1);
+ StateTable::State &newState = m_allStates[stateIndex];
+ newState.name = addString(state->id);
+ newState.parent = currentParent();
+
+ switch (state->type) {
+ case DocumentModel::State::Normal:
+ newState.type = StateTable::State::Normal;
+ break;
+ case DocumentModel::State::Parallel:
+ newState.type = StateTable::State::Parallel;
+ break;
+ case DocumentModel::State::Final:
+ newState.type = StateTable::State::Final;
+ newState.doneData = generate(state->doneData);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ m_parents.append(stateIndex);
+
+ if (!state->dataElements.isEmpty()) {
+ if (m_bindLate) {
+ newState.initInstructions = startNewSequence();
+ generate(state->dataElements);
+ endSequence();
+ } else {
+ m_dataElements.append(state->dataElements);
+ }
+ }
+
+ newState.entryInstructions = generate(state->onEntry);
+ newState.exitInstructions = generate(state->onExit);
+ if (!state->invokes.isEmpty()) {
+ QVector<int> factoryIds;
+ for (DocumentModel::Invoke *invoke : qAsConst(state->invokes)) {
+ auto ctxt = createContext(QStringLiteral("invoke"));
+ QVector<QScxmlExecutableContent::StringId> namelist;
+ for (const QString &name : qAsConst(invoke->namelist))
+ namelist += addString(name);
+ QVector<QScxmlExecutableContent::ParameterInfo> params;
+ for (DocumentModel::Param *param : qAsConst(invoke->params)) {
+ QScxmlExecutableContent::ParameterInfo p;
+ p.name = addString(param->name);
+ p.expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"),
+ param->expr);
+ p.location = addString(param->location);
+ params.append(p);
+ }
+ QScxmlExecutableContent::ContainerId finalize =
+ QScxmlExecutableContent::NoInstruction;
+ if (!invoke->finalize.isEmpty()) {
+ finalize = startNewSequence();
+ visit(&invoke->finalize);
+ endSequence();
+ }
+ auto srcexpr = createEvaluatorString(QStringLiteral("invoke"),
+ QStringLiteral("srcexpr"),
+ invoke->srcexpr);
+ QScxmlExecutableContent::InvokeInfo invokeInfo;
+ invokeInfo.id = addString(invoke->id);
+ invokeInfo.prefix = addString(state->id + QStringLiteral(".session-"));
+ invokeInfo.location = addString(invoke->idLocation);
+ invokeInfo.context = ctxt;
+ invokeInfo.expr = srcexpr;
+ invokeInfo.finalize = finalize;
+ invokeInfo.autoforward = invoke->autoforward;
+ const int factoryId = createFactoryId(invokeInfo, namelist, params,
+ invoke->content);
+ Q_ASSERT(factoryId >= 0);
+ factoryIds.append(factoryId);
+ m_stateTable.maxServiceId = std::max(m_stateTable.maxServiceId, factoryId);
+ }
+ newState.serviceFactoryIds = addArray(factoryIds);
+ }
+
+ visit(state->children);
+
+ QVector<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *sot : qAsConst(state->children)) {
+ if (auto s = sot->asAbstractState()) {
+ childStates.append(s);
+ }
+ }
+ newState.childStates = addStates(childStates);
+ newState.transitions = addArray(m_transitionsForState.at(stateIndex));
+ if (state->initialTransition) {
+ visit(state->initialTransition);
+ newState.initialTransition = m_transitionsForState.at(stateIndex).last();
+ }
+ m_parents.removeLast();
+
+ return false;
+ }
+
+ bool visit(DocumentModel::Transition *transition) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ const int transitionIndex = m_docTransitionIndices.value(transition, -1);
+ Q_ASSERT(transitionIndex != -1);
+ StateTable::Transition &newTransition = m_allTransitions[transitionIndex];
+ const int parentIndex = currentParent();
+ if (parentIndex != -1) {
+ m_transitionsForState[parentIndex].append(transitionIndex);
+ }
+ newTransition.source = parentIndex;
+
+ if (transition->condition) {
+ newTransition.condition = createEvaluatorBool(QStringLiteral("transition"),
+ QStringLiteral("cond"),
+ *transition->condition.data());
+ }
+
+ switch (transition->type) {
+ case DocumentModel::Transition::External:
+ newTransition.type = StateTable::Transition::External;
+ break;
+ case DocumentModel::Transition::Internal:
+ newTransition.type = StateTable::Transition::Internal;
+ break;
+ case DocumentModel::Transition::Synthetic:
+ newTransition.type = StateTable::Transition::Synthetic;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ if (!transition->instructionsOnTransition.isEmpty()) {
+ m_currentTransition = transitionIndex;
+ newTransition.transitionInstructions = startNewSequence();
+ visit(&transition->instructionsOnTransition);
+ endSequence();
+ m_currentTransition = -1;
+ }
+
+ newTransition.targets = addStates(transition->targetStates);
+
+ QVector<int> eventIds;
+ for (const QString &event : qAsConst(transition->events))
+ eventIds.push_back(addString(event));
+
+ newTransition.events = addArray(eventIds);
+
+ return false;
+ }
+
+ bool visit(DocumentModel::HistoryState *historyState) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ const int stateIndex = m_docStatesIndices.value(historyState, -1);
+ Q_ASSERT(stateIndex != -1);
+ StateTable::State &newState = m_allStates[stateIndex];
+ newState.name = addString(historyState->id);
+ newState.parent = currentParent();
+
+ switch (historyState->type) {
+ case DocumentModel::HistoryState::Shallow:
+ newState.type = StateTable::State::ShallowHistory;
+ break;
+ case DocumentModel::HistoryState::Deep:
+ newState.type = StateTable::State::DeepHistory;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ m_parents.append(stateIndex);
+ visit(historyState->children);
+ m_parents.removeLast();
+ newState.transitions = addArray(m_transitionsForState.at(stateIndex));
+ return false;
+ }
+
+ bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Send>(Send::calculateExtraSize(node->params.size(),
+ node->namelist.size()));
+ instr->instructionLocation = createContext(QStringLiteral("send"));
+ instr->event = addString(node->event);
+ instr->eventexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("eventexpr"),
+ node->eventexpr);
+ instr->type = addString(node->type);
+ instr->typeexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("typeexpr"),
+ node->typeexpr);
+ instr->target = addString(node->target);
+ instr->targetexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("targetexpr"),
+ node->targetexpr);
+ instr->id = addString(node->id);
+ instr->idLocation = addString(node->idLocation);
+ instr->delay = addString(node->delay);
+ instr->delayexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("delayexpr"),
+ node->delayexpr);
+ instr->content = addString(node->content);
+ instr->contentexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("contentexpr"),
+ node->contentexpr);
+ generate(&instr->namelist, node->namelist);
+ generate(instr->params(), node->params);
+ return false;
+ }
+
+ void visit(DocumentModel::Raise *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Raise>();
+ instr->event = addString(node->event);
+ }
+
+ void visit(DocumentModel::Log *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Log>();
+ instr->label = addString(node->label);
+ instr->expr = createEvaluatorString(QStringLiteral("log"),
+ QStringLiteral("expr"),
+ node->expr);
+ }
+
+ void visit(DocumentModel::Script *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<JavaScript>();
+ instr->go = createEvaluatorVoid(QStringLiteral("script"),
+ QStringLiteral("source"),
+ node->content);
+ }
+
+ void visit(DocumentModel::Assign *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Assign>();
+ auto ctxt = createContext(QStringLiteral("assign"), QStringLiteral("expr"), node->expr);
+ instr->expression = addAssignment(node->location, node->expr, ctxt);
+ }
+
+ bool visit(DocumentModel::If *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<If>(node->conditions.size());
+ instr->conditions.count = node->conditions.size();
+ auto it = instr->conditions.data();
+ QString tag = QStringLiteral("if");
+ for (int i = 0, ei = node->conditions.size(); i != ei; ++i) {
+ *it++ = createEvaluatorBool(tag, QStringLiteral("cond"), node->conditions.at(i));
+ if (i == 0) {
+ tag = QStringLiteral("elif");
+ }
+ }
+ auto outSequences = m_instructions.add<InstructionSequences>();
+ generate(outSequences, node->blocks);
+ return false;
+ }
+
+ bool visit(DocumentModel::Foreach *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Foreach>();
+ auto ctxt = createContextString(QStringLiteral("foreach"));
+ instr->doIt = addForeach(node->array, node->item, node->index, ctxt);
+ startSequence(&instr->block);
+ visit(&node->block);
+ endSequence();
+ return false;
+ }
+
+ void visit(DocumentModel::Cancel *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Cancel>();
+ instr->sendid = addString(node->sendid);
+ instr->sendidexpr = createEvaluatorString(QStringLiteral("cancel"),
+ QStringLiteral("sendidexpr"),
+ node->sendidexpr);
+ }
+
+protected:
+ static int paramSize() { return sizeof(ParameterInfo) / sizeof(qint32); }
+
+ ContainerId generate(const DocumentModel::DoneData *node)
+ {
+ auto id = m_instructions.newContainerId();
+ DoneData *doneData;
+ if (node) {
+ doneData = m_instructions.add<DoneData>(node->params.size() * paramSize());
+ doneData->contents = addString(node->contents);
+ doneData->expr = createEvaluatorString(QStringLiteral("donedata"),
+ QStringLiteral("expr"),
+ node->expr);
+ generate(&doneData->params, node->params);
+ } else {
+ doneData = m_instructions.add<DoneData>();
+ doneData->contents = NoString;
+ doneData->expr = NoEvaluator;
+ doneData->params.count = 0;
+ }
+ doneData->location = createContext(QStringLiteral("final"));
+ return id;
+ }
+
+ StringId createContext(const QString &instrName)
+ {
+ return addString(createContextString(instrName));
+ }
+
+ void generate(const QVector<DocumentModel::DataElement *> &dataElements)
+ {
+ for (DocumentModel::DataElement *el : dataElements) {
+ auto ctxt = createContext(QStringLiteral("data"), QStringLiteral("expr"), el->expr);
+ auto evaluator = addDataElement(el->id, el->expr, ctxt);
+ if (evaluator != NoEvaluator) {
+ auto instr = m_instructions.add<QScxmlExecutableContent::Initialize>();
+ instr->expression = evaluator;
+ }
+ }
+ }
+
+ ContainerId generate(const DocumentModel::InstructionSequences &inSequences)
+ {
+ if (inSequences.isEmpty())
+ return NoInstruction;
+
+ auto id = m_instructions.newContainerId();
+ auto outSequences = m_instructions.add<InstructionSequences>();
+ generate(outSequences, inSequences);
+ return id;
+ }
+
+ void generate(Array<ParameterInfo> *out, const QVector<DocumentModel::Param *> &in)
+ {
+ out->count = in.size();
+ ParameterInfo *it = out->data();
+ for (DocumentModel::Param *f : in) {
+ it->name = addString(f->name);
+ it->expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"),
+ f->expr);
+ it->location = addString(f->location);
+ ++it;
+ }
+ }
+
+ void generate(InstructionSequences *outSequences,
+ const DocumentModel::InstructionSequences &inSequences)
+ {
+ int sequencesOffset = m_instructions.offset(outSequences);
+ int sequenceCount = 0;
+ int entryCount = 0;
+ for (DocumentModel::InstructionSequence *sequence : inSequences) {
+ ++sequenceCount;
+ startNewSequence();
+ visit(sequence);
+ entryCount += endSequence()->size();
+ }
+ outSequences = m_instructions.at<InstructionSequences>(sequencesOffset);
+ outSequences->sequenceCount = sequenceCount;
+ outSequences->entryCount = entryCount;
+ }
+
+ void generate(Array<StringId> *out, const QStringList &in)
+ {
+ out->count = in.size();
+ StringId *it = out->data();
+ for (const QString &str : in) {
+ *it++ = addString(str);
+ }
+ }
+
+ ContainerId startNewSequence()
+ {
+ auto id = m_instructions.newContainerId();
+ auto sequence = m_instructions.add<InstructionSequence>();
+ startSequence(sequence);
+ return id;
+ }
+
+ void startSequence(InstructionSequence *sequence)
+ {
+ SequenceInfo info;
+ info.location = m_instructions.offset(sequence);
+ info.entryCount = 0;
+ m_activeSequences.push_back(info);
+ m_instructions.setSequenceInfo(&m_activeSequences.last());
+ sequence->instructionType = Instruction::Sequence;
+ sequence->entryCount = -1; // checked in endSequence
+ }
+
+ InstructionSequence *endSequence()
+ {
+ SequenceInfo info = m_activeSequences.back();
+ m_activeSequences.pop_back();
+ m_instructions.setSequenceInfo(m_activeSequences.isEmpty() ? Q_NULLPTR :
+ &m_activeSequences.last());
+
+ auto sequence = m_instructions.at<InstructionSequence>(info.location);
+ Q_ASSERT(sequence->entryCount == -1); // set in startSequence
+ sequence->entryCount = info.entryCount;
+ if (!m_activeSequences.isEmpty())
+ m_activeSequences.last().entryCount += info.entryCount;
+ return sequence;
+ }
+
+ EvaluatorId createEvaluatorString(const QString &instrName, const QString &attrName,
+ const QString &expr)
+ {
+ if (!expr.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.stringEvaluators.insert(id, expr);
+ return id;
+ } else {
+ return addEvaluator(expr, createContext(instrName, attrName, expr));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ EvaluatorId createEvaluatorBool(const QString &instrName, const QString &attrName,
+ const QString &cond)
+ {
+ if (!cond.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.boolEvaluators.insert(id, cond);
+ return id;
+ } else {
+ return addEvaluator(cond, createContext(instrName, attrName, cond));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ EvaluatorId createEvaluatorVariant(const QString &instrName, const QString &attrName,
+ const QString &expr)
+ {
+ if (!expr.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.variantEvaluators.insert(id, expr);
+ return id;
+ } else {
+ return addEvaluator(expr, createContext(instrName, attrName, expr));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ EvaluatorId createEvaluatorVoid(const QString &instrName, const QString &attrName,
+ const QString &stuff)
+ {
+ if (!stuff.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.voidEvaluators.insert(id, stuff);
+ return id;
+ } else {
+ return addEvaluator(stuff, createContext(instrName, attrName, stuff));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ GeneratedTableData *tableData(const QVector<int> &stateMachineTable);
+
+ StringId addString(const QString &str)
+ { return str.isEmpty() ? NoString : m_stringTable.add(str); }
+
+ void setInitialSetup(ContainerId id)
+ { m_tableData.theInitialSetup = id; }
+
+ void setName(const QString &name)
+ { m_tableData.theName = addString(name); }
+
+ bool isCppDataModel() const
+ { return m_isCppDataModel; }
+
+ int addStates(const QVector<DocumentModel::AbstractState *> &states)
+ {
+ QVector<int> array;
+ for (auto *s : states) {
+ int si = m_docStatesIndices.value(s, -1);
+ Q_ASSERT(si != -1);
+ array.push_back(si);
+ }
+
+ return addArray(array);
+ }
+
+ int addArray(const QVector<int> &array)
+ {
+ if (array.isEmpty())
+ return -1;
+
+ const int res = m_arrays.size();
+ m_arrays.push_back(array.size());
+ m_arrays.append(array);
+ return res;
+ }
+
+ int currentParent() const
+ {
+ return m_parents.last();
+ }
+
+ QString createContextString(const QString &instrName) const
+ {
+ if (m_currentTransition != -1) {
+ QString state;
+ int parent = m_allTransitions.at(m_currentTransition).source;
+ if (parent != -1) {
+ QString parentName = QStringLiteral("(none)");
+ int name = m_allStates.at(parent).name;
+ if (name != -1) {
+ parentName = m_stringTable.item(name);
+ }
+ state = QStringLiteral(" of state '%1'").arg(parentName);
+ }
+ return QStringLiteral("%1 instruction in transition %3").arg(instrName, state);
+ } else {
+ QString parentName = QStringLiteral("(none)");
+ const int parent = currentParent();
+ if (parent != -1) {
+ const int name = m_allStates.at(parent).name;
+ if (name != -1) {
+ parentName = m_stringTable.item(name);
+ }
+ }
+ return QStringLiteral("%1 instruction in state %2").arg(instrName, parentName);
+ }
+ }
+
+ QString createContext(const QString &instrName, const QString &attrName,
+ const QString &attrValue) const
+ {
+ const QString location = createContextString(instrName);
+ return QStringLiteral("%1 with %2=\"%3\"").arg(location, attrName, attrValue);
+ }
+
+ EvaluatorId addEvaluator(const QString &expr, const QString &context)
+ {
+ EvaluatorInfo ei;
+ ei.expr = addString(expr);
+ ei.context = addString(context);
+ return m_evaluators.add(ei);
+ }
+
+ EvaluatorId addAssignment(const QString &dest, const QString &expr, const QString &context)
+ {
+ AssignmentInfo ai;
+ ai.dest = addString(dest);
+ ai.expr = addString(expr);
+ ai.context = addString(context);
+ return m_assignments.add(ai);
+ }
+
+ EvaluatorId addForeach(const QString &array, const QString &item, const QString &index,
+ const QString &context)
+ {
+ ForeachInfo fi;
+ fi.array = addString(array);
+ fi.item = addString(item);
+ fi.index = addString(index);
+ fi.context = addString(context);
+ return m_foreaches.add(fi);
+ }
+
+ EvaluatorId addDataElement(const QString &id, const QString &expr, const QString &context)
+ {
+ auto str = addString(id);
+ if (!m_dataIds.contains(str))
+ m_dataIds.append(str);
+ if (expr.isEmpty())
+ return NoEvaluator;
+
+ return addAssignment(id, expr, context);
+ }
+
+private:
+ template <class Container, typename T, typename U>
+ class Table {
+ Container &elements;
+ QMap<T, int> indexForElement;
+
+ public:
+ Table(Container &storage)
+ : elements(storage)
+ {}
+
+ U add(const T &s, bool uniqueOnly = true) {
+ int pos = uniqueOnly ? indexForElement.value(s, -1) : -1;
+ if (pos == -1) {
+ pos = elements.size();
+ elements.append(s);
+ indexForElement.insert(s, pos);
+ }
+ return pos;
+ }
+
+ Container data() {
+ return elements;
+ }
+
+ const T &item(U pos) const {
+ return elements.at(pos);
+ }
+ };
+
+ struct SequenceInfo {
+ int location;
+ qint32 entryCount; // the amount of qint32's that the instructions take up
+ };
+
+ class InstructionStorage {
+ public:
+ InstructionStorage(QVector<qint32> &storage)
+ : m_instr(storage)
+ , m_info(Q_NULLPTR)
+ {}
+
+ ContainerId newContainerId() const { return m_instr.size(); }
+
+ template <typename T>
+ T *add(int extra = 0)
+ {
+ const int pos = m_instr.size();
+ const int size = sizeof(T) / sizeof(qint32) + extra;
+ if (m_info)
+ m_info->entryCount += size;
+ m_instr.resize(pos + size);
+ T *instr = at<T>(pos);
+ Q_ASSERT(instr->instructionType == 0);
+ instr->instructionType = T::kind();
+ return instr;
+ }
+
+ int offset(Instruction *instr) const
+ {
+ return reinterpret_cast<qint32 *>(instr) - m_instr.data();
+ }
+
+ template <typename T>
+ T *at(int offset)
+ {
+ return reinterpret_cast<T *>(&m_instr[offset]);
+ }
+
+ void setSequenceInfo(SequenceInfo *info)
+ {
+ m_info = info;
+ }
+
+ private:
+ QVector<qint32> &m_instr;
+ SequenceInfo *m_info;
+ };
+
+ QVector<SequenceInfo> m_activeSequences;
+
+ GeneratedTableData::CreateFactoryId createFactoryId;
+ GeneratedTableData &m_tableData;
+ GeneratedTableData::DataModelInfo &m_dataModelInfo;
+ Table<QStringList, QString, StringId> m_stringTable;
+ InstructionStorage m_instructions;
+ Table<QVector<EvaluatorInfo>, EvaluatorInfo, EvaluatorId> m_evaluators;
+ Table<QVector<AssignmentInfo>, AssignmentInfo, EvaluatorId> m_assignments;
+ Table<QVector<ForeachInfo>, ForeachInfo, EvaluatorId> m_foreaches;
+ QVector<StringId> &m_dataIds;
+ bool m_isCppDataModel = false;
+
+ StateTable m_stateTable;
+ QVector<int> m_parents;
+ QVector<qint32> m_arrays;
+
+ QVector<StateTable::Transition> m_allTransitions;
+ QHash<DocumentModel::Transition *, int> m_docTransitionIndices;
+ QVector<StateTable::State> m_allStates;
+ QHash<DocumentModel::AbstractState *, int> m_docStatesIndices;
+ QVector<QVector<int>> m_transitionsForState;
+
+ int m_currentTransition = StateTable::InvalidIndex;
+ bool m_bindLate = false;
+ QVector<DocumentModel::DataElement *> m_dataElements;
+ Table<QStringList, QString, int> m_stateNames;
+};
+
+} // anonymous namespace
QScxmlTableData::~QScxmlTableData()
{}
-QT_END_NAMESPACE
+void GeneratedTableData::build(DocumentModel::ScxmlDocument *doc,
+ GeneratedTableData *table,
+ MetaDataInfo *metaDataInfo,
+ DataModelInfo *dataModelInfo,
+ GeneratedTableData::CreateFactoryId func)
+{
+ TableDataBuilder builder(*table, *metaDataInfo, *dataModelInfo, func);
+ builder.buildTableData(doc);
+}
+
+QString GeneratedTableData::toString(const int *stateMachineTable)
+{
+ QString result;
+ QTextStream out(&result);
+
+ const StateTable *st = reinterpret_cast<const StateTable *>(stateMachineTable);
+
+ out << "{" << endl
+ << "\t0x" << hex << st->version << dec << ", // version" << endl
+ << "\t" << st->name << ", // name" << endl
+ << "\t" << st->dataModel << ", // data-model" << endl
+ << "\t" << st->childStates << ", // child states array offset" << endl
+ << "\t" << st->initialTransition << ", // transition to initial states" << endl
+ << "\t" << st->initialSetup << ", // initial setup" << endl
+ << "\t" << st->binding << ", // binding" << endl
+ << "\t" << st->maxServiceId << ", // maxServiceId" << endl
+ << "\t" << st->stateOffset << ", " << st->stateCount
+ << ", // state offset and count" << endl
+ << "\t" << st->transitionOffset << ", " << st->transitionCount
+ << ", // transition offset and count" << endl
+ << "\t" << st->arrayOffset << ", " << st->arraySize << ", // array offset and size" << endl
+ << endl;
+
+ out << "\t// States:" << endl;
+ for (int i = 0; i < st->stateCount; ++i) {
+ const StateTable::State &s = st->state(i);
+ out << "\t"
+ << s.name << ", "
+ << s.parent << ", "
+ << s.type << ", "
+ << s.initialTransition << ", "
+ << s.initInstructions << ", "
+ << s.entryInstructions << ", "
+ << s.exitInstructions << ", "
+ << s.doneData << ", "
+ << s.childStates << ", "
+ << s.transitions << ", "
+ << s.serviceFactoryIds << ","
+ << endl;
+ }
+
+ out << endl
+ << "\t// Transitions:" << endl;
+ for (int i = 0; i < st->transitionCount; ++i) {
+ auto t = st->transition(i);
+ out << "\t"
+ << t.events << ", "
+ << t.condition << ", "
+ << t.type << ", "
+ << t.source << ", "
+ << t.targets << ", "
+ << t.transitionInstructions << ", "
+ << endl ;
+ }
+
+ out << endl
+ << "\t// Arrays:" << endl;
+ int nextStart = 0;
+ while (nextStart < st->arraySize) {
+ const StateTable::Array a = st->array(nextStart);
+ out << "\t" << a.size() << ", ";
+ for (int j = 0; j < a.size(); ++j) {
+ out << a[j] << ", ";
+ }
+ out << endl;
+ nextStart += a.size() + 1;
+ }
+
+ out << hex;
+ out << endl
+ << "\t0x" << StateTable::terminator << " // terminator" << endl
+ << "}";
+
+ return result;
+}
+
+QString GeneratedTableData::string(StringId id) const
+{
+ return id == NoString ? QString() : theStrings.at(id);
+}
+
+InstructionId *GeneratedTableData::instructions() const
+{
+ return const_cast<InstructionId *>(theInstructions.data());
+}
+
+EvaluatorInfo GeneratedTableData::evaluatorInfo(EvaluatorId evaluatorId) const
+{
+ return theEvaluators[evaluatorId];
+}
+
+AssignmentInfo GeneratedTableData::assignmentInfo(EvaluatorId assignmentId) const
+{
+ return theAssignments[assignmentId];
+}
+
+ForeachInfo GeneratedTableData::foreachInfo(EvaluatorId foreachId) const
+{
+ return theForeaches[foreachId];
+}
+
+StringId *GeneratedTableData::dataNames(int *count) const
+{
+ Q_ASSERT(count);
+ *count = theDataNameIds.size();
+ return const_cast<StringId *>(theDataNameIds.data());
+}
+
+ContainerId GeneratedTableData::initialSetup() const
+{
+ return theInitialSetup;
+}
+
+QString GeneratedTableData::name() const
+{
+ return string(theName);
+}
+
+const qint32 *GeneratedTableData::stateMachineTable() const
+{
+ return theStateMachineTable.constData();
+}
+
+QScxmlInvokableServiceFactory *GeneratedTableData::serviceFactory(int id) const
+{
+ Q_UNUSED(id);
+ return nullptr;
+}
diff --git a/src/scxml/qscxmltabledata.h b/src/scxml/qscxmltabledata.h
index 59fb105..5fcabd1 100644
--- a/src/scxml/qscxmltabledata.h
+++ b/src/scxml/qscxmltabledata.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSCXMLTABLEDATA_P
-#define QSCXMLTABLEDATA_P
+#ifndef QSCXMLTABLEDATA_H
+#define QSCXMLTABLEDATA_H
#include <QtScxml/qscxmlexecutablecontent.h>
#include <QString>
@@ -49,13 +49,15 @@
QT_BEGIN_NAMESPACE
+class QScxmlInvokableServiceFactory;
+
class Q_SCXML_EXPORT QScxmlTableData
{
public:
virtual ~QScxmlTableData();
virtual QString string(QScxmlExecutableContent::StringId id) const = 0;
- virtual QScxmlExecutableContent::Instructions instructions() const = 0;
+ virtual QScxmlExecutableContent::InstructionId *instructions() const = 0;
virtual QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const = 0;
virtual QScxmlExecutableContent::AssignmentInfo assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const = 0;
virtual QScxmlExecutableContent::ForeachInfo foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const = 0;
@@ -63,8 +65,11 @@ public:
virtual QScxmlExecutableContent::ContainerId initialSetup() const = 0;
virtual QString name() const = 0;
+
+ virtual const qint32 *stateMachineTable() const = 0;
+ virtual QScxmlInvokableServiceFactory *serviceFactory(int id) const = 0;
};
QT_END_NAMESPACE
-#endif // QSCXMLTABLEDATA_P
+#endif // QSCXMLTABLEDATA_H
diff --git a/src/scxml/qscxmltabledata_p.h b/src/scxml/qscxmltabledata_p.h
new file mode 100644
index 0000000..2c825db
--- /dev/null
+++ b/src/scxml/qscxmltabledata_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCXMLTABLEDATA_P_H
+#define QSCXMLTABLEDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScxml/qscxmltabledata.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvector.h>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+class QScxmlInvokableServiceFactory;
+
+namespace DocumentModel {
+struct ScxmlDocument;
+}
+
+namespace QScxmlInternal {
+class Q_SCXML_EXPORT GeneratedTableData: public QScxmlTableData
+{
+public:
+ typedef std::function<
+ int(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &params,
+ QSharedPointer<DocumentModel::ScxmlDocument> content)
+ > CreateFactoryId;
+
+ struct MetaDataInfo {
+ QStringList stateNames;
+ };
+
+ struct DataModelInfo {
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> stringEvaluators;
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> boolEvaluators;
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> variantEvaluators;
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> voidEvaluators;
+ };
+
+public:
+ static void build(DocumentModel::ScxmlDocument *doc, GeneratedTableData *table,
+ MetaDataInfo *metaDataInfo, DataModelInfo *dataModelInfo,
+ CreateFactoryId func);
+ static QString toString(const int *stateMachineTable);
+
+public:
+ QString string(QScxmlExecutableContent::StringId id) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::InstructionId *instructions() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(
+ QScxmlExecutableContent::EvaluatorId evaluatorId) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::AssignmentInfo assignmentInfo(
+ QScxmlExecutableContent::EvaluatorId assignmentId) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::ForeachInfo foreachInfo(
+ QScxmlExecutableContent::EvaluatorId foreachId) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::StringId *dataNames(int *count) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ const qint32 *stateMachineTable() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlInvokableServiceFactory *serviceFactory(int id) const Q_DECL_OVERRIDE;
+
+public:
+ QVector<qint32> theStateMachineTable;
+ QStringList theStrings;
+ QVector<qint32> theInstructions;
+ QVector<QScxmlExecutableContent::EvaluatorInfo> theEvaluators;
+ QVector<QScxmlExecutableContent::AssignmentInfo> theAssignments;
+ QVector<QScxmlExecutableContent::ForeachInfo> theForeaches;
+ QVector<QScxmlExecutableContent::StringId> theDataNameIds;
+ QScxmlExecutableContent::EvaluatorId theInitialSetup;
+ int theName;
+};
+} // QScxmlInternal namespace
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLTABLEDATA_P_H
diff --git a/src/scxml/scxml.pro b/src/scxml/scxml.pro
index 40601e3..1f5bc25 100644
--- a/src/scxml/scxml.pro
+++ b/src/scxml/scxml.pro
@@ -1,6 +1,6 @@
TARGET = QtScxml
QT = core-private qml-private
-MODULE_CONFIG += c++11
+MODULE_CONFIG += c++11 qscxmlc
load(qt_module)
@@ -10,8 +10,8 @@ CONFIG += $$MODULE_CONFIG
DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
HEADERS += \
- qscxmlparser.h \
- qscxmlparser_p.h \
+ qscxmlcompiler.h \
+ qscxmlcompiler_p.h \
qscxmlstatemachine.h \
qscxmlstatemachine_p.h \
qscxmlglobals.h \
@@ -25,16 +25,17 @@ HEADERS += \
qscxmlevent_p.h \
qscxmldatamodel.h \
qscxmldatamodel_p.h \
- qscxmlqstates.h \
- qscxmlqstates_p.h \
qscxmlcppdatamodel_p.h \
qscxmlcppdatamodel.h \
qscxmlerror.h \
+ qscxmlinvokableservice_p.h \
qscxmlinvokableservice.h \
- qscxmltabledata.h
+ qscxmltabledata.h \
+ qscxmltabledata_p.h \
+ qscxmlstatemachineinfo_p.h
SOURCES += \
- qscxmlparser.cpp \
+ qscxmlcompiler.cpp \
qscxmlstatemachine.cpp \
qscxmlnulldatamodel.cpp \
qscxmlecmascriptdatamodel.cpp \
@@ -42,11 +43,11 @@ SOURCES += \
qscxmlexecutablecontent.cpp \
qscxmlevent.cpp \
qscxmldatamodel.cpp \
- qscxmlqstates.cpp \
qscxmlcppdatamodel.cpp \
qscxmlerror.cpp \
qscxmlinvokableservice.cpp \
- qscxmltabledata.cpp
+ qscxmltabledata.cpp \
+ qscxmlstatemachineinfo.cpp
FEATURES += ../../mkspecs/features/qscxmlc.prf
features.files = $$FEATURES
diff --git a/tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.json b/tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.json
new file mode 100644
index 0000000..2b697c0
--- /dev/null
+++ b/tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.json
@@ -0,0 +1,4 @@
+{
+ "initialConfiguration" : ["pass"],
+ "events" : []
+}
diff --git a/tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.scxml b/tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.scxml
new file mode 100644
index 0000000..4cfa34a
--- /dev/null
+++ b/tests/3rdparty/scion-tests/scxml-test-framework/test/content-expr-in-send/test0.txml.scxml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early"
+ name="content-expr-in-send" datamodel="ecmascript">
+ <state id="top">
+ <onentry>
+ <send event="timeout" delay="2s"/>
+ <send event="to_second">
+ <content>blah</content>
+ </send>
+ </onentry>
+ <state id="first">
+ <transition event="to_second" target="second"/>
+ </state>
+
+ <state id="second">
+ <onentry>
+ <send event="to_pass">
+ <content expr="_event.data"/>
+ </send>
+ </onentry>
+ </state>
+ <transition event="to_pass" cond="_event.data=='blah'" target="pass"/>
+ <transition event="timeout" target="fail"/>
+ </state>
+
+ <final id="pass"><onentry><log label="Outcome" expr="'pass'"/></onentry></final>
+ <final id="fail"><onentry><log label="Outcome" expr="'fail'"/></onentry></final>
+</scxml>
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/auto.pro b/tests/auto/auto.pro
index a6dd3ae..60576d5 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -3,6 +3,6 @@ SUBDIRS = cmake\
compiled\
dynamicmetaobject\
parser\
- qscxmlc\
scion\
- statemachine
+ statemachine \
+ statemachineinfo
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index 8518d09..9c9e098 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -11,3 +11,5 @@ include("${_Qt5CTestMacros}")
test_module_includes(
Scxml QScxmlEvent
)
+
+expect_pass(test_qtscxml_module)
diff --git a/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt b/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt
new file mode 100644
index 0000000..e2cf03a
--- /dev/null
+++ b/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(test_qtscxml_module)
+
+find_package(Qt5Scxml REQUIRED)
+
+set(MAIN_SRCS main.cpp)
+qt5_add_statecharts(MAIN_SRCS
+ ../../compiled/connection.scxml
+
+ # unused, just for testing whether it's possible to pass multiple files
+ ../../compiled/topmachine.scxml
+)
+add_executable(mainapp ${MAIN_SRCS})
+target_include_directories(mainapp PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(mainapp Qt5::Scxml)
diff --git a/tests/auto/cmake/test_qtscxml_module/main.cpp b/tests/auto/cmake/test_qtscxml_module/main.cpp
new file mode 100644
index 0000000..2d081df
--- /dev/null
+++ b/tests/auto/cmake/test_qtscxml_module/main.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "connection.h"
+
+int main(int argc, char **argv)
+{
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+
+ Connection connection;
+ Q_UNUSED(connection);
+ return 0;
+}
diff --git a/tests/auto/compiled/anonymousstate.scxml b/tests/auto/compiled/anonymousstate.scxml
index 016b339..60adfb0 100644
--- a/tests/auto/compiled/anonymousstate.scxml
+++ b/tests/auto/compiled/anonymousstate.scxml
@@ -1,5 +1,4 @@
<?xml version="1.0" ?>
-<!-- enable-qt-mode: no -->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="Pinball2" datamodel="ecmascript">
<state>
diff --git a/tests/auto/compiled/compiled.pro b/tests/auto/compiled/compiled.pro
index 0fd3db9..713f484 100644
--- a/tests/auto/compiled/compiled.pro
+++ b/tests/auto/compiled/compiled.pro
@@ -18,6 +18,8 @@ STATECHARTS = \
anonymousstate.scxml \
submachineunicodename.scxml \
datainnulldatamodel.scxml \
- initialhistory.scxml
+ initialhistory.scxml \
+ connection.scxml \
+ topmachine.scxml
-load(qscxmlc)
+RESOURCES = tst_compiled.qrc
diff --git a/tests/auto/parser/data/qtmode.scxml b/tests/auto/compiled/connection.scxml
index c5df8b0..c5654e5 100644
--- a/tests/auto/parser/data/qtmode.scxml
+++ b/tests/auto/compiled/connection.scxml
@@ -28,19 +28,15 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="test1">
- <state id="a">
- <transition event="a"/>
- <transition event="void"/>
- <transition event="aChanged"/>
- <transition event="finished"/>
- <transition event="done.state.a"/>
- <transition event="done.state.double"/>
- </state>
- <state id="int"/>
- <state id="objectName"/>
- <state id="foo"/>
- <state id="fooChanged"/>
+ name="Connection">
+ <parallel id="top">
+ <state id="a">
+ <state id="a1"/>
+ <state id="a2"/>
+ </state>
+ <state id="b">
+ <final id="final"/>
+ </state>
+ </parallel>
</scxml>
diff --git a/tests/auto/compiled/eventnames1.scxml b/tests/auto/compiled/eventnames1.scxml
index 150f6c0..475b1b0 100644
--- a/tests/auto/compiled/eventnames1.scxml
+++ b/tests/auto/compiled/eventnames1.scxml
@@ -28,7 +28,7 @@
**
****************************************************************************/
-->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="eventnames1">
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="eventnames1" initial="a">
<state id="a">
<onentry>
<send event="valid"/>
@@ -42,5 +42,8 @@
<transition event="-valid-"/>
<transition event="also valid"/>
<transition event="&#x00ff;valid"/>
+ <transition event="näl" target="b"/>
+ </state>
+ <state id="b">
</state>
</scxml>
diff --git a/tests/auto/compiled/eventnames2.scxml b/tests/auto/compiled/eventnames2.scxml
index 32c80d5..49a5e98 100644
--- a/tests/auto/compiled/eventnames2.scxml
+++ b/tests/auto/compiled/eventnames2.scxml
@@ -28,7 +28,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="eventnames2">
<state id="a">
<onentry>
diff --git a/tests/auto/compiled/submachineA.scxml b/tests/auto/compiled/submachineA.scxml
new file mode 100644
index 0000000..0924b2e
--- /dev/null
+++ b/tests/auto/compiled/submachineA.scxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine">
+ <final id="topState"/>
+</scxml>
diff --git a/tests/auto/compiled/submachineB.scxml b/tests/auto/compiled/submachineB.scxml
new file mode 100644
index 0000000..0924b2e
--- /dev/null
+++ b/tests/auto/compiled/submachineB.scxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine">
+ <final id="topState"/>
+</scxml>
diff --git a/tests/auto/compiled/submachineunicodename.scxml b/tests/auto/compiled/submachineunicodename.scxml
index b62e751..9cee0e3 100644
--- a/tests/auto/compiled/submachineunicodename.scxml
+++ b/tests/auto/compiled/submachineunicodename.scxml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
diff --git a/tests/auto/compiled/topmachine.scxml b/tests/auto/compiled/topmachine.scxml
new file mode 100644
index 0000000..b733c6a
--- /dev/null
+++ b/tests/auto/compiled/topmachine.scxml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="TopMachine" datamodel="ecmascript">
+ <datamodel>
+ <data id="doneCounter" expr="0"/>
+ </datamodel>
+ <state id="topState">
+ <invoke type="scxml" id="submachine.1" src="file:submachineA.scxml"/>
+ <invoke type="scxml" id="submachine.2" src="file:submachineA.scxml"/>
+ <invoke type="scxml" id="submachine.3" src="file:submachineB.scxml"/>
+ <transition event="done.invoke">
+ <assign location="doneCounter" expr="doneCounter + 1"/>
+ <if cond="doneCounter === 3">
+ <send event="goToFinal" delay="1s"/>
+ </if>
+ </transition>
+ <transition event="goToFinal" target="finalState"/>
+ </state>
+ <final id="finalState"/>
+</scxml>
diff --git a/tests/auto/compiled/tst_compiled.cpp b/tests/auto/compiled/tst_compiled.cpp
index 49174e3..ab590f7 100644
--- a/tests/auto/compiled/tst_compiled.cpp
+++ b/tests/auto/compiled/tst_compiled.cpp
@@ -29,12 +29,16 @@
#include <QtTest>
#include <QObject>
#include <QXmlStreamReader>
-#include <QtScxml/qscxmlparser.h>
+#include <QtScxml/qscxmlcompiler.h>
#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/qscxmlinvokableservice.h>
#include "ids1.h"
#include "statemachineunicodename.h"
#include "datainnulldatamodel.h"
#include "submachineunicodename.h"
+#include "eventnames1.h"
+#include "connection.h"
+#include "topmachine.h"
Q_DECLARE_METATYPE(QScxmlError);
@@ -48,27 +52,34 @@ private Q_SLOTS:
void stateNames();
void nullDataInit();
void subMachineUnicodeName();
+ void unicodeEventName();
+ void connection();
+ void myConnection();
+ void topMachine();
+ void topMachineDynamic();
+ void publicSignals();
};
void tst_Compiled::stateNames()
{
ids1 stateMachine;
+ // The states have to be appear in document order:
QStringList ids1States({
- "_",
- "_VALID",
- "__valid",
- "foo-bar",
"foo.bar",
+ "foo-bar",
"foo_bar",
- "n_0xe4_l",
+ "_",
"näl",
+ "n_0xe4_l",
+ "_VALID",
+ "__valid",
"qÿ̀i",
});
QCOMPARE(stateMachine.stateNames(false), ids1States);
- foreach (const QString &state, ids1States) {
+ for (const QString &state : qAsConst(ids1States)) {
QVariant prop = stateMachine.property(state.toUtf8().constData());
QVERIFY(!prop.isNull());
QVERIFY(prop.isValid());
@@ -94,10 +105,207 @@ void tst_Compiled::nullDataInit()
void tst_Compiled::subMachineUnicodeName()
{
Directions1 directions;
- QVERIFY(directions.init());
- QVariant prop = directions.property("änywhere");
- QVERIFY(!prop.isNull());
- QVERIFY(prop.isValid());
+ QSignalSpy stableStateSpy(&directions, SIGNAL(reachedStableState()));
+ directions.start();
+ stableStateSpy.wait(5000);
+ QScxmlInvokableService *service = directions.invokedServices().value(0);
+ QVERIFY(service);
+ QCOMPARE(service->name(), QString("änywhere"));
+}
+
+void tst_Compiled::unicodeEventName()
+{
+ eventnames1 names;
+ QSignalSpy stableStateSpy(&names, SIGNAL(reachedStableState()));
+ names.start();
+
+ stableStateSpy.wait(5000);
+
+ QCOMPARE(names.activeStateNames(), QStringList(QLatin1String("a")));
+ names.submitEvent("näl");
+ stableStateSpy.wait(5000);
+ QCOMPARE(names.activeStateNames(), QStringList(QLatin1String("b")));
+}
+
+class Receiver : public QObject
+{
+ Q_OBJECT
+public slots:
+ void receive(bool enabled)
+ {
+ received = received || enabled;
+ }
+
+ void enter()
+ {
+ entered = true;
+ }
+
+ void exit()
+ {
+ exited = true;
+ }
+
+public:
+ bool received = false;
+ bool entered = false;
+ bool exited = false;
+};
+
+void tst_Compiled::connection()
+{
+ Connection stateMachine;
+
+ Receiver receiverA;
+ Receiver receiverA1;
+ Receiver receiverA2;
+ Receiver receiverB;
+ Receiver receiverFinal;
+
+ QMetaObject::Connection conA = stateMachine.connectToState("a", &receiverA, SLOT(receive(bool)));
+ QMetaObject::Connection conA1 = stateMachine.connectToState("a1", &receiverA1, SLOT(receive(bool)));
+ QMetaObject::Connection conA2 = stateMachine.connectToState("a2", &receiverA2, SLOT(receive(bool)));
+ QMetaObject::Connection conB = stateMachine.connectToState("b", &receiverB, SLOT(receive(bool)));
+ QMetaObject::Connection conFinal = stateMachine.connectToState("final", &receiverFinal, SLOT(receive(bool)));
+
+ typedef QScxmlStateMachine QXSM;
+ QMetaObject::Connection aEntry = stateMachine.connectToState("a", QXSM::onEntry(&receiverA, "enter"));
+ QMetaObject::Connection aExit = stateMachine.connectToState("a", QXSM::onExit(&receiverA, "exit"));
+
+ QVERIFY(aEntry);
+ QVERIFY(aExit);
+
+ QVERIFY(conA);
+ QVERIFY(conA1);
+ QVERIFY(conA2);
+ QVERIFY(conB);
+ QVERIFY(conFinal);
+
+ stateMachine.start();
+
+ QTRY_VERIFY(receiverA.received);
+ QTRY_VERIFY(receiverA1.received);
+ QTRY_VERIFY(!receiverA2.received);
+ QTRY_VERIFY(receiverB.received);
+ QTRY_VERIFY(receiverFinal.received);
+
+ QVERIFY(disconnect(conA));
+ QVERIFY(disconnect(conA1));
+ QVERIFY(disconnect(conA2));
+ QVERIFY(disconnect(conB));
+ QVERIFY(disconnect(conFinal));
+
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ QVERIFY(receiverA.entered);
+ QVERIFY(!receiverA.exited);
+ QVERIFY(disconnect(aEntry));
+ QVERIFY(disconnect(aExit));
+#endif
+}
+
+class MyConnection : public Connection
+{
+ Q_OBJECT
+public:
+ MyConnection(QObject *parent = 0)
+ : Connection(parent)
+ {}
+};
+
+void tst_Compiled::myConnection()
+{
+ MyConnection stateMachine;
+
+ Receiver receiverA;
+ Receiver receiverA1;
+ Receiver receiverA2;
+ Receiver receiverB;
+ Receiver receiverFinal;
+
+ QMetaObject::Connection conA = stateMachine.connectToState("a", &receiverA, SLOT(receive(bool)));
+ QMetaObject::Connection conA1 = stateMachine.connectToState("a1", &receiverA1, SLOT(receive(bool)));
+ QMetaObject::Connection conA2 = stateMachine.connectToState("a2", &receiverA2, SLOT(receive(bool)));
+ QMetaObject::Connection conB = stateMachine.connectToState("b", &receiverB, SLOT(receive(bool)));
+ QMetaObject::Connection conFinal = stateMachine.connectToState("final", &receiverFinal, SLOT(receive(bool)));
+
+ QVERIFY(conA);
+ QVERIFY(conA1);
+ QVERIFY(conA2);
+ QVERIFY(conB);
+ QVERIFY(conFinal);
+
+ stateMachine.start();
+
+ QTRY_VERIFY(receiverA.received);
+ QTRY_VERIFY(receiverA1.received);
+ QTRY_VERIFY(!receiverA2.received);
+ QTRY_VERIFY(receiverB.received);
+ QTRY_VERIFY(receiverFinal.received);
+
+ QVERIFY(disconnect(conA));
+ QVERIFY(disconnect(conA1));
+ QVERIFY(disconnect(conA2));
+ QVERIFY(disconnect(conB));
+ QVERIFY(disconnect(conFinal));
+}
+
+void tst_Compiled::topMachine()
+{
+ TopMachine stateMachine;
+ int doneCounter = 0;
+ int invokableServicesCount = 0;
+
+ stateMachine.connectToEvent("done.invoke.submachine", [&doneCounter](const QScxmlEvent &) {
+ ++doneCounter;
+ });
+
+ QObject::connect(&stateMachine, &QScxmlStateMachine::invokedServicesChanged,
+ [&invokableServicesCount](const QVector<QScxmlInvokableService *> &services) {
+ invokableServicesCount = services.count();
+ });
+
+ stateMachine.start();
+
+ QTRY_COMPARE(invokableServicesCount, 3);
+ QTRY_COMPARE(doneCounter, 3);
+ QCOMPARE(stateMachine.invokedServices().count(), 3);
+ QTRY_COMPARE(invokableServicesCount, 0);
+}
+
+void tst_Compiled::topMachineDynamic()
+{
+ QScopedPointer<QScxmlStateMachine> stateMachine(
+ QScxmlStateMachine::fromFile(QString(":/topmachine.scxml")));
+ QVERIFY(!stateMachine.isNull());
+ int doneCounter = 0;
+ int invokableServicesCount = 0;
+
+ stateMachine->connectToEvent("done.invoke.submachine", [&doneCounter](const QScxmlEvent &) {
+ ++doneCounter;
+ });
+
+ QObject::connect(stateMachine.data(), &QScxmlStateMachine::invokedServicesChanged,
+ [&invokableServicesCount](const QVector<QScxmlInvokableService *> &services) {
+ invokableServicesCount = services.count();
+ });
+
+ stateMachine->start();
+
+ QTRY_COMPARE(invokableServicesCount, 3);
+ QTRY_COMPARE(doneCounter, 3);
+ QCOMPARE(stateMachine->invokedServices().count(), 3);
+ QTRY_COMPARE(invokableServicesCount, 0);
+}
+
+void tst_Compiled::publicSignals()
+{
+ const QMetaObject *connectionMeta = &Connection::staticMetaObject;
+ int index = connectionMeta->indexOfSignal("aChanged(bool)");
+ QVERIFY(index >= 0);
+
+ QMetaMethod aChanged = connectionMeta->method(index);
+ QVERIFY(aChanged.isValid());
+ QCOMPARE(aChanged.access(), QMetaMethod::Public);
}
QTEST_MAIN(tst_Compiled)
diff --git a/tests/auto/compiled/tst_compiled.qrc b/tests/auto/compiled/tst_compiled.qrc
new file mode 100644
index 0000000..43d355c
--- /dev/null
+++ b/tests/auto/compiled/tst_compiled.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>topmachine.scxml</file>
+ <file>submachineA.scxml</file>
+ <file>submachineB.scxml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/dynamicmetaobject/dynamicmetaobject.pro b/tests/auto/dynamicmetaobject/dynamicmetaobject.pro
index f78e5ac..5ec4e5f 100644
--- a/tests/auto/dynamicmetaobject/dynamicmetaobject.pro
+++ b/tests/auto/dynamicmetaobject/dynamicmetaobject.pro
@@ -11,5 +11,3 @@ RESOURCES += tst_dynamicmetaobject.qrc
SOURCES += \
tst_dynamicmetaobject.cpp
-
-load(qscxmlc)
diff --git a/tests/auto/dynamicmetaobject/mediaplayer.scxml b/tests/auto/dynamicmetaobject/mediaplayer.scxml
index 025245a..ca68039 100644
--- a/tests/auto/dynamicmetaobject/mediaplayer.scxml
+++ b/tests/auto/dynamicmetaobject/mediaplayer.scxml
@@ -50,7 +50,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
@@ -76,13 +75,13 @@
<state id="playing">
<onentry>
<assign location="media" expr="_event.data.media"/>
- <send type="qt:signal" event="playbackStarted">
+ <send event="playbackStarted">
<param name="media" expr="media"/>
</send>
</onentry>
<onexit>
- <send type="qt:signal" event="playbackStopped">
+ <send event="playbackStopped">
<param name="media" expr="media"/>
</send>
</onexit>
diff --git a/tests/auto/dynamicmetaobject/test1.scxml b/tests/auto/dynamicmetaobject/test1.scxml
index 72fd592..531b1d5 100644
--- a/tests/auto/dynamicmetaobject/test1.scxml
+++ b/tests/auto/dynamicmetaobject/test1.scxml
@@ -32,6 +32,7 @@
name="test1" datamodel="ecmascript">
<state id="a">
<transition event="foo"/>
+ <transition event="näl" target="b"/>
</state>
<state id="b">
<transition event="foo"/>
diff --git a/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp b/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp
index 7610dd7..83fceeb 100644
--- a/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp
+++ b/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp
@@ -44,41 +44,28 @@ void tst_DynamicMetaObject::dynamicPartCheck_data()
{
QTest::addColumn<QString>("scxmlFileName");
QTest::addColumn<QStringList>("expectedProperties");
- QTest::addColumn<QStringList>("expectedMethods");
QTest::addColumn<QStringList>("expectedSignals");
- QTest::addColumn<QStringList>("expectedSlots");
{ // test1.scxml
const QStringList expectedProperties = { QLatin1String("a"), QLatin1String("b") };
- const QStringList expectedMethods = { };
const QStringList expectedSignals = { QLatin1String("aChanged(bool)"),
QLatin1String("bChanged(bool)") };
- const QStringList expectedSlots = { };
QTest::newRow("test1") << QString(":/tst_dynamicmetaobject/test1.scxml")
<< expectedProperties
- << expectedMethods
- << expectedSignals
- << expectedSlots;
+ << expectedSignals;
}
{ // mediaplayer.scxml
const QStringList expectedProperties = { QLatin1String("stopped"), QLatin1String("playing") };
- const QStringList expectedMethods = { };
const QStringList expectedSignals = { QLatin1String("stoppedChanged(bool)"),
- QLatin1String("playingChanged(bool)"),
- QLatin1String("playbackStarted(QVariant)"),
- QLatin1String("playbackStopped(QVariant)") };
- const QStringList expectedSlots = { QLatin1String("tap()"),
- QLatin1String("tap(QVariant)") };
+ QLatin1String("playingChanged(bool)") };
QTest::newRow("mediaplayer") << QString(":/tst_dynamicmetaobject/mediaplayer.scxml")
<< expectedProperties
- << expectedMethods
- << expectedSignals
- << expectedSlots;
+ << expectedSignals;
}
}
@@ -86,9 +73,7 @@ void tst_DynamicMetaObject::dynamicPartCheck()
{
QFETCH(QString, scxmlFileName);
QFETCH(QStringList, expectedProperties);
- QFETCH(QStringList, expectedMethods);
QFETCH(QStringList, expectedSignals);
- QFETCH(QStringList, expectedSlots);
QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(scxmlFileName));
QVERIFY(!stateMachine.isNull());
@@ -98,42 +83,25 @@ void tst_DynamicMetaObject::dynamicPartCheck()
QStringList dynamicProperties;
for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); i++) {
- QMetaProperty metaProperty = metaObject->property(i);
+ const QMetaProperty metaProperty = metaObject->property(i);
dynamicProperties << metaProperty.name();
}
- QStringList dynamicMethods, dynamicSignals, dynamicSlots;
+ QStringList dynamicSignals;
for (int i = metaObject->methodOffset(); i < metaObject->methodCount(); i++) {
- QMetaMethod metaMethod = metaObject->method(i);
- switch (metaMethod.methodType()) {
- case QMetaMethod::Method:
- dynamicMethods << metaMethod.methodSignature();
- break;
- case QMetaMethod::Signal:
+ const QMetaMethod metaMethod = metaObject->method(i);
+ if (metaMethod.methodType() == QMetaMethod::Signal)
dynamicSignals << metaMethod.methodSignature();
- break;
- case QMetaMethod::Slot:
- dynamicSlots << metaMethod.methodSignature();
- break;
- default:
- break;
- }
}
// TODO: remove sorting when we drop QSet internally
expectedProperties.sort();
- expectedMethods.sort();
expectedSignals.sort();
- expectedSlots.sort();
dynamicProperties.sort();
- dynamicMethods.sort();
dynamicSignals.sort();
- dynamicSlots.sort();
QCOMPARE(dynamicProperties, expectedProperties);
- QCOMPARE(dynamicMethods, expectedMethods);
QCOMPARE(dynamicSignals, expectedSignals);
- QCOMPARE(dynamicSlots, expectedSlots);
}
QTEST_MAIN(tst_DynamicMetaObject)
diff --git a/tests/auto/parser/data/badInitial.scxml b/tests/auto/parser/data/badInitial.scxml
new file mode 100644
index 0000000..8247857
--- /dev/null
+++ b/tests/auto/parser/data/badInitial.scxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ initial="stopped"
+>
+ <script>
+ <initial>
+
+ <state id="stopped">
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/badInitial.scxml.errors b/tests/auto/parser/data/badInitial.scxml.errors
new file mode 100644
index 0000000..fa2eba9
--- /dev/null
+++ b/tests/auto/parser/data/badInitial.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/badInitial.scxml:8:13: error: Unexpected element initial
+:/tst_parser/data/badInitial.scxml:12:8: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/qscxmlc/data/commentInScript.scxml b/tests/auto/parser/data/commentInScript.scxml
index 2af8286..2a663b1 100644
--- a/tests/auto/qscxmlc/data/commentInScript.scxml
+++ b/tests/auto/parser/data/commentInScript.scxml
@@ -1,15 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- enable-qt-mode: yes -->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
- name="CommentInScript"
- datamodel="ecmascript"
>
- <datamodel>
- <data id="media"/>
- </datamodel>
-
<script><!--
function isValidMedia() {
var m = _event.data.media
diff --git a/tests/auto/parser/data/commentInScript.scxml.errors b/tests/auto/parser/data/commentInScript.scxml.errors
new file mode 100644
index 0000000..d12133f
--- /dev/null
+++ b/tests/auto/parser/data/commentInScript.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/commentInScript.scxml:6:0: error: Error parsing SCXML file: Premature end of document.
diff --git a/tests/auto/qscxmlc/data/empty.scxml b/tests/auto/parser/data/empty.scxml
index 10c563b..10c563b 100644
--- a/tests/auto/qscxmlc/data/empty.scxml
+++ b/tests/auto/parser/data/empty.scxml
diff --git a/tests/auto/parser/data/empty.scxml.errors b/tests/auto/parser/data/empty.scxml.errors
new file mode 100644
index 0000000..ce558e0
--- /dev/null
+++ b/tests/auto/parser/data/empty.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/empty.scxml:2:0: error: Missing root element
diff --git a/tests/auto/parser/data/eventnames.scxml.errors b/tests/auto/parser/data/eventnames.scxml.errors
new file mode 100644
index 0000000..9b4d5de
--- /dev/null
+++ b/tests/auto/parser/data/eventnames.scxml.errors
@@ -0,0 +1,5 @@
+:/tst_parser/data/eventnames.scxml:50:38: error: '.invalid' is not a valid event
+:/tst_parser/data/eventnames.scxml:51:38: error: 'invalid.' is not a valid event
+:/tst_parser/data/eventnames.scxml:39:36: error: '.invalid' is not a valid event
+:/tst_parser/data/eventnames.scxml:40:36: error: 'invalid.' is not a valid event
+:/tst_parser/data/eventnames.scxml:41:36: error: 'in valid' is not a valid event
diff --git a/tests/auto/parser/data/ids1.scxml.errors b/tests/auto/parser/data/ids1.scxml.errors
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/auto/parser/data/ids1.scxml.errors
diff --git a/tests/auto/parser/data/ids2.scxml b/tests/auto/parser/data/ids2.scxml
index 228b487..5378fcb 100644
--- a/tests/auto/parser/data/ids2.scxml
+++ b/tests/auto/parser/data/ids2.scxml
@@ -28,7 +28,6 @@
**
****************************************************************************/
-->
-<!-- enable-qt-mode: yes -->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="ids2">
<state id="foo.bar"/>
<state id="foo-bar"/>
diff --git a/tests/auto/parser/data/ids2.scxml.errors b/tests/auto/parser/data/ids2.scxml.errors
new file mode 100644
index 0000000..3ca729e
--- /dev/null
+++ b/tests/auto/parser/data/ids2.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/ids2.scxml:35:19: error: '1' is not a valid XML ID
diff --git a/tests/auto/parser/data/invalidContent.scxml b/tests/auto/parser/data/invalidContent.scxml
new file mode 100644
index 0000000..232c0ae
--- /dev/null
+++ b/tests/auto/parser/data/invalidContent.scxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <content />
+ <scxml name="anywhere" version="1.0">
+ <state id="here">
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidContent.scxml.errors b/tests/auto/parser/data/invalidContent.scxml.errors
new file mode 100644
index 0000000..77db37e
--- /dev/null
+++ b/tests/auto/parser/data/invalidContent.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/invalidContent.scxml:9:53: error: Unexpected element scxml
+:/tst_parser/data/invalidContent.scxml:13:22: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/invalidRoot1.scxml b/tests/auto/parser/data/invalidRoot1.scxml
new file mode 100644
index 0000000..f21ad1e
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot1.scxml
@@ -0,0 +1,5 @@
+<?xml version="1.0" ?>
+<Scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidRoot1.scxml.errors b/tests/auto/parser/data/invalidRoot1.scxml.errors
new file mode 100644
index 0000000..17a4705
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot1.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/invalidRoot1.scxml:2:61: error: Unknown element Scxml
+:/tst_parser/data/invalidRoot1.scxml:5:8: error: Missing root element
diff --git a/tests/auto/parser/data/invalidRoot2.scxml b/tests/auto/parser/data/invalidRoot2.scxml
new file mode 100644
index 0000000..260373f
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot2.scxml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <content>
+ <scxmn name="anywhere" version="1.0">
+ <state id="there"datamodel
+ <transition event="goHere" target="here"/>
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidRoot2.scxml.errors b/tests/auto/parser/data/invalidRoot2.scxml.errors
new file mode 100644
index 0000000..10aeada
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot2.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/invalidRoot2.scxml:9:53: error: Unknown element scxmn
+:/tst_parser/data/invalidRoot2.scxml:10:38: error: Error parsing SCXML file: Expected '>' or '/', but got '[a-zA-Z]'.
diff --git a/tests/auto/parser/data/invalidRoot3.scxml b/tests/auto/parser/data/invalidRoot3.scxml
new file mode 100644
index 0000000..62dab58
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot3.scxml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxma
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="anyplace">
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidRoot3.scxml.errors b/tests/auto/parser/data/invalidRoot3.scxml.errors
new file mode 100644
index 0000000..dd1cf6c
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot3.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/invalidRoot3.scxml:5:1: error: Unknown element scxma
+:/tst_parser/data/invalidRoot3.scxml:8:8: error: Missing root element
diff --git a/tests/auto/parser/data/invalidRoot6.scxml b/tests/auto/parser/data/invalidRoot6.scxml
new file mode 100644
index 0000000..c0633e8
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot6.scxml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <content>
+ |scxml name="anywhere" version="1.0">
+ <state id="here">
+ </state>
+ <state id="there">
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidRoot6.scxml.errors b/tests/auto/parser/data/invalidRoot6.scxml.errors
new file mode 100644
index 0000000..3e6d92d
--- /dev/null
+++ b/tests/auto/parser/data/invalidRoot6.scxml.errors
@@ -0,0 +1,3 @@
+:/tst_parser/data/invalidRoot6.scxml:10:37: error: Unexpected element state
+:/tst_parser/data/invalidRoot6.scxml:12:38: error: Unexpected element state
+:/tst_parser/data/invalidRoot6.scxml:14:24: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/invalidUnicode1.scxml b/tests/auto/parser/data/invalidUnicode1.scxml
new file mode 100644
index 0000000..8e5493a
--- /dev/null
+++ b/tests/auto/parser/data/invalidUnicode1.scxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <content>
+ <scxml name="anywhere" version="1.0">
+ <state id="here">
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidUnicode1.scxml.errors b/tests/auto/parser/data/invalidUnicode1.scxml.errors
new file mode 100644
index 0000000..bde9c02
--- /dev/null
+++ b/tests/auto/parser/data/invalidUnicode1.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/invalidUnicode1.scxml:7:52: error: Error parsing SCXML file: Unexpected ''.
diff --git a/tests/auto/parser/data/invalidXmlHeader1.scxml b/tests/auto/parser/data/invalidXmlHeader1.scxml
new file mode 100644
index 0000000..dc0ef36
--- /dev/null
+++ b/tests/auto/parser/data/invalidXmlHeader1.scxml
Binary files differ
diff --git a/tests/auto/parser/data/invalidXmlHeader1.scxml.errors b/tests/auto/parser/data/invalidXmlHeader1.scxml.errors
new file mode 100644
index 0000000..0fc223b
--- /dev/null
+++ b/tests/auto/parser/data/invalidXmlHeader1.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/invalidXmlHeader1.scxml:1:66: error: Missing root element
diff --git a/tests/auto/parser/data/invalidXmlHeader2.scxml b/tests/auto/parser/data/invalidXmlHeader2.scxml
new file mode 100644
index 0000000..a9ffeb5
--- /dev/null
+++ b/tests/auto/parser/data/invalidXmlHeader2.scxml
@@ -0,0 +1,5 @@
+<ry>l version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/invalidXmlHeader2.scxml.errors b/tests/auto/parser/data/invalidXmlHeader2.scxml.errors
new file mode 100644
index 0000000..c969f8b
--- /dev/null
+++ b/tests/auto/parser/data/invalidXmlHeader2.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/invalidXmlHeader2.scxml:6:0: error: Missing root element
diff --git a/tests/auto/qscxmlc/data/invalidstatemachinename.scxml b/tests/auto/parser/data/invalidstatemachinename.scxml
index e47a699..e47a699 100644
--- a/tests/auto/qscxmlc/data/invalidstatemachinename.scxml
+++ b/tests/auto/parser/data/invalidstatemachinename.scxml
diff --git a/tests/auto/parser/data/invalidstatemachinename.scxml.errors b/tests/auto/parser/data/invalidstatemachinename.scxml.errors
new file mode 100644
index 0000000..dbd1a34
--- /dev/null
+++ b/tests/auto/parser/data/invalidstatemachinename.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/invalidstatemachinename.scxml:3:42: error: Missing root element
diff --git a/tests/auto/qscxmlc/data/misplacedinvoke.scxml b/tests/auto/parser/data/misplacedinvoke.scxml
index fe5e5de..fe5e5de 100644
--- a/tests/auto/qscxmlc/data/misplacedinvoke.scxml
+++ b/tests/auto/parser/data/misplacedinvoke.scxml
diff --git a/tests/auto/parser/data/misplacedinvoke.scxml.errors b/tests/auto/parser/data/misplacedinvoke.scxml.errors
new file mode 100644
index 0000000..31c6fa0
--- /dev/null
+++ b/tests/auto/parser/data/misplacedinvoke.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/misplacedinvoke.scxml:12:26: error: Unexpected element finalize
diff --git a/tests/auto/parser/data/namespaces1.scxml.errors b/tests/auto/parser/data/namespaces1.scxml.errors
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/auto/parser/data/namespaces1.scxml.errors
diff --git a/tests/auto/qscxmlc/data/nestedScxml.scxml b/tests/auto/parser/data/nestedScxml.scxml
index 2f96347..2f96347 100644
--- a/tests/auto/qscxmlc/data/nestedScxml.scxml
+++ b/tests/auto/parser/data/nestedScxml.scxml
diff --git a/tests/auto/parser/data/nestedScxml.scxml.errors b/tests/auto/parser/data/nestedScxml.scxml.errors
new file mode 100644
index 0000000..1735855
--- /dev/null
+++ b/tests/auto/parser/data/nestedScxml.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/nestedScxml.scxml:5:60: error: Unexpected element scxml
diff --git a/tests/auto/parser/data/noContentInInvoke1.scxml b/tests/auto/parser/data/noContentInInvoke1.scxml
new file mode 100644
index 0000000..6d5fc1a
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke1.scxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <contenscxml name="anywhere" version="1.0">
+ <state id="there">
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/noContentInInvoke1.scxml.errors b/tests/auto/parser/data/noContentInInvoke1.scxml.errors
new file mode 100644
index 0000000..ec7a4a4
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke1.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/noContentInInvoke1.scxml:8:55: error: Unknown element contenscxml
+:/tst_parser/data/noContentInInvoke1.scxml:11:24: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/noContentInInvoke2.scxml b/tests/auto/parser/data/noContentInInvoke2.scxml
new file mode 100644
index 0000000..50e6c37
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke2.scxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ initial="anyplace"
+>
+ <state id="anyp\ace">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <contenon use t <sl name="anywhere version="1.0">
+ <state id="here">
+ </state>
+ </scxml>
+ * Redistr </content>
+ </invoke>
+ , </state>
+</scxml>
diff --git a/tests/auto/parser/data/noContentInInvoke2.scxml.errors b/tests/auto/parser/data/noContentInInvoke2.scxml.errors
new file mode 100644
index 0000000..8fb90e5
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke2.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/noContentInInvoke2.scxml:9:27: error: Error parsing SCXML file: Expected '=', but got '[a-zA-Z]'.
diff --git a/tests/auto/parser/data/noContentInInvoke3.scxml b/tests/auto/parser/data/noContentInInvoke3.scxml
new file mode 100644
index 0000000..918e120
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke3.scxml
Binary files differ
diff --git a/tests/auto/parser/data/noContentInInvoke3.scxml.errors b/tests/auto/parser/data/noContentInInvoke3.scxml.errors
new file mode 100644
index 0000000..e35ed7a
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke3.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/noContentInInvoke3.scxml:10:25: error: Error parsing SCXML file: Expected '=', but got '[a-zA-Z]'.
diff --git a/tests/auto/parser/data/noContentInInvoke4.scxml b/tests/auto/parser/data/noContentInInvoke4.scxml
new file mode 100644
index 0000000..2782daf
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke4.scxml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ initial="anyplacE"
+>
+ <state id="anyplace">
+ <transition event="goSomewhere" target="somewhere"/>
+
+ <state id="s* LIMITomewhere">
+ <invoke type="http://www.w$.org/TR/scxml/">
+  <content>
+ <scxml name="anywhere" version="1.0">
+ <sta e id="here">
+ </state>
+ </scxm8>
+ </content>
+ </invoke>
+ </state>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/noContentInInvoke4.scxml.errors b/tests/auto/parser/data/noContentInInvoke4.scxml.errors
new file mode 100644
index 0000000..cac148d
--- /dev/null
+++ b/tests/auto/parser/data/noContentInInvoke4.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/noContentInInvoke4.scxml:12:6: error: Error parsing SCXML file: Unexpected ''.
diff --git a/tests/auto/parser/data/prematureEndOfDocument1.scxml b/tests/auto/parser/data/prematureEndOfDocument1.scxml
new file mode 100644
index 0000000..63d938c
--- /dev/null
+++ b/tests/auto/parser/data/prematureEndOfDocument1.scxml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- enable-qt-mode: yes m->
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="anyplace">
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/prematureEndOfDocument1.scxml.errors b/tests/auto/parser/data/prematureEndOfDocument1.scxml.errors
new file mode 100644
index 0000000..24ae3e9
--- /dev/null
+++ b/tests/auto/parser/data/prematureEndOfDocument1.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/prematureEndOfDocument1.scxml:2:0: error: Missing root element
diff --git a/tests/auto/parser/data/prematureEndOfDocument2.scxml b/tests/auto/parser/data/prematureEndOfDocument2.scxml
new file mode 100644
index 0000000..e1fde76
--- /dev/null
+++ b/tests/auto/parser/data/prematureEndOfDocument2.scxml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="anyplace">
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <content>
+<!-- <scxml name="anywhere" version="1.0">
+ <state id="here">
+ <transition event="goThere" target="there"/>
+ </state>
+ <state id="there">
+ <transition event="goHere" target="here"/>
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/prematureEndOfDocument2.scxml.errors b/tests/auto/parser/data/prematureEndOfDocument2.scxml.errors
new file mode 100644
index 0000000..e6532cc
--- /dev/null
+++ b/tests/auto/parser/data/prematureEndOfDocument2.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/prematureEndOfDocument2.scxml:10:0: error: Error parsing SCXML file: Premature end of document.
diff --git a/tests/auto/parser/data/scxml1.scxml.errors b/tests/auto/parser/data/scxml1.scxml.errors
new file mode 100644
index 0000000..870d167
--- /dev/null
+++ b/tests/auto/parser/data/scxml1.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/scxml1.scxml:32:36: error: Unsupported data model 'foo' in scxml
+:/tst_parser/data/scxml1.scxml:34:30: error: Unexpected element scxml
diff --git a/tests/auto/parser/data/scxml2.scxml.errors b/tests/auto/parser/data/scxml2.scxml.errors
new file mode 100644
index 0000000..a9f0e30
--- /dev/null
+++ b/tests/auto/parser/data/scxml2.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/scxml2.scxml:32:34: error: Unsupperted binding type 'foo'
diff --git a/tests/auto/parser/data/syntaxErrors1.scxml b/tests/auto/parser/data/syntaxErrors1.scxml
new file mode 100644
index 0000000..64e25f0
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors1.scxml
@@ -0,0 +1,14 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="begin">
+ <transitifn event="OPER.MINUS" target="negated1" />
+ </state>
+
+ <state id="negated1">
+ nameate>
+ </state>
+ <transition event="OP.INSERT">
+ <assigZ location="long_expr" expr="long_expr+'+'" />
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors1.scxml.errors b/tests/auto/parser/data/syntaxErrors1.scxml.errors
new file mode 100644
index 0000000..ac98603
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors1.scxml.errors
@@ -0,0 +1,3 @@
+:/tst_parser/data/syntaxErrors1.scxml:4:59: error: Unknown element transitifn
+:/tst_parser/data/syntaxErrors1.scxml:11:64: error: Unknown element assigZ
+:/tst_parser/data/syntaxErrors1.scxml:13:12: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors10.scxml b/tests/auto/parser/data/syntaxErrors10.scxml
new file mode 100644
index 0000000..257cc26
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors10.scxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="workflow">
+ <transition event="updateLights">
+ |if cond="In('lightImpulseOn')">
+ <raise event="dings"/>
+ <else/>
+ <raise event="turnOffGameOver"/>
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors10.scxml.errors b/tests/auto/parser/data/syntaxErrors10.scxml.errors
new file mode 100644
index 0000000..786d6ce
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors10.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors10.scxml:7:19: error: Unexpected element else
+:/tst_parser/data/syntaxErrors10.scxml:9:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors11.scxml b/tests/auto/parser/data/syntaxErrors11.scxml
new file mode 100644
index 0000000..0ab2c58
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors11.scxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ <transition event="OP.INSERT">
+ <if cond="_event.data.operator == 'OPER.PLUS'">
+ <Assign location="long_expr" expr="long_expr+'-'" />
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors11.scxml.errors b/tests/auto/parser/data/syntaxErrors11.scxml.errors
new file mode 100644
index 0000000..3d34249
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors11.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/syntaxErrors11.scxml:6:68: error: Unknown element Assign
diff --git a/tests/auto/parser/data/syntaxErrors12.scxml b/tests/auto/parser/data/syntaxErrors12.scxml
new file mode 100644
index 0000000..9295c01
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors12.scxml
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ <transition event="OP.INSERT">
+ <if cond="_event.data.operator == 'OPER.PLUS'">
+ <assign location="long_expr">expr="long_expr+'+'" />
+ <elseif cond="_event.data.operator=='OPER.MINUS'" />
+ <assign location="long_expr" expr="long_expr+'-'" />
+ <elseif cond="_event.data.operator=='OPER.STAR'" />
+ <assign location="long_expr" expr="long_expr+'*'" />
+ <elseif cond="_event.data.operator=='OPER.DIV'" />
+ <assign location="long_expr" expr="long_expr+'/'" />
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors12.scxml.errors b/tests/auto/parser/data/syntaxErrors12.scxml.errors
new file mode 100644
index 0000000..df27955
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors12.scxml.errors
@@ -0,0 +1,7 @@
+:/tst_parser/data/syntaxErrors12.scxml:7:64: error: Unexpected element elseif
+:/tst_parser/data/syntaxErrors12.scxml:8:68: error: Unexpected element assign
+:/tst_parser/data/syntaxErrors12.scxml:9:63: error: Unexpected element elseif
+:/tst_parser/data/syntaxErrors12.scxml:10:68: error: Unexpected element assign
+:/tst_parser/data/syntaxErrors12.scxml:11:62: error: Unexpected element elseif
+:/tst_parser/data/syntaxErrors12.scxml:12:68: error: Unexpected element assign
+:/tst_parser/data/syntaxErrors12.scxml:13:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors13.scxml b/tests/auto/parser/data/syntaxErrors13.scxml
new file mode 100644
index 0000000..ea0728c
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors13.scxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ <transition event="OP.INSERT">
+ |if cond="_event.data.operator == 'OPER.PLUS'">
+ <assign location="long_expr" expr="long_expr+'+'" />
+ <elseif cond="_event.data.operator=='OPER.MINUS'" />
+ <assign location="long_expr" expr="long_expr+'-'" />
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors13.scxml.errors b/tests/auto/parser/data/syntaxErrors13.scxml.errors
new file mode 100644
index 0000000..e3104b2
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors13.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors13.scxml:7:64: error: Unexpected element elseif
+:/tst_parser/data/syntaxErrors13.scxml:9:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors14.scxml b/tests/auto/parser/data/syntaxErrors14.scxml
new file mode 100644
index 0000000..33890df
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors14.scxml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ <transition event="OP.INSERT">
+ <If cond="_event.data.operator == 'OPER.PLUS'">
+ <assign location="long_expr" expr="long_expr+'+'" />
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors14.scxml.errors b/tests/auto/parser/data/syntaxErrors14.scxml.errors
new file mode 100644
index 0000000..47bf05c
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors14.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors14.scxml:5:59: error: Unknown element If
+:/tst_parser/data/syntaxErrors14.scxml:7:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors15.scxml b/tests/auto/parser/data/syntaxErrors15.scxml
new file mode 100644
index 0000000..aeeb686
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors15.scxml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ http://www.w3.org/2005/07/scxmlscxml name="anywhere" version="1.0">
+ <state id="here">
+ </state>
+ <state id="there">
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors15.scxml.errors b/tests/auto/parser/data/syntaxErrors15.scxml.errors
new file mode 100644
index 0000000..406e65e
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors15.scxml.errors
@@ -0,0 +1,3 @@
+:/tst_parser/data/syntaxErrors15.scxml:9:37: error: Unexpected element state
+:/tst_parser/data/syntaxErrors15.scxml:11:38: error: Unexpected element state
+:/tst_parser/data/syntaxErrors15.scxml:13:24: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors2.scxml b/tests/auto/parser/data/syntaxErrors2.scxml
new file mode 100644
index 0000000..3c97fa7
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors2.scxml
@@ -0,0 +1,14 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="lightImpulseGenerator">
+ <transition event="scheduleNewImpulse">
+ <if cond="In('offState')">
+ <send event="lightImpulse" id="lightId" delay="1s"/>
+ <elseif cond="In('hurryStateOff')"/>
+ <send>event="lightImpulse" id="lightId" delay="500ms"/>
+ <else/>
+ <send event="lightImpulse" id="lightId" delay="200ms"/>
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors2.scxml.errors b/tests/auto/parser/data/syntaxErrors2.scxml.errors
new file mode 100644
index 0000000..a86e782
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors2.scxml.errors
@@ -0,0 +1,3 @@
+:/tst_parser/data/syntaxErrors2.scxml:9:19: error: Unexpected element else
+:/tst_parser/data/syntaxErrors2.scxml:10:71: error: Unexpected element send
+:/tst_parser/data/syntaxErrors2.scxml:11:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors3.scxml b/tests/auto/parser/data/syntaxErrors3.scxml
new file mode 100644
index 0000000..e419851
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors3.scxml
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ <state id="ready">
+id="result">
+ </state>
+ </state>
+ <state id="o]erand1">
+ </state>
+ <state id="operand2">
+ <transition event="EQUALS" target="result">
+ </transition>
+ </state>
+
+ <transition event="OP.INSERT">
+ <assige location="long_expr" expr="long_expr+'*'" />
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors3.scxml.errors b/tests/auto/parser/data/syntaxErrors3.scxml.errors
new file mode 100644
index 0000000..23479f9
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors3.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors3.scxml:16:64: error: Unknown element assige
+:/tst_parser/data/syntaxErrors3.scxml:18:12: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors4.scxml b/tests/auto/parser/data/syntaxErrors4.scxml
new file mode 100644
index 0000000..4fb8ab9
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors4.scxml
@@ -0,0 +1,17 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper" initial="on">
+ <state id="on">
+ <state id="negated2">
+ <state id="operand2">
+ </state>
+ </state>
+ <transition event="OP.INSERT">
+ <if cond="_event.data.operator == 'OPER.PLUS'">
+ <assign location="long_expr" expr="long_expr+'+'" />
+ <elsDif cond="_event.data.operator=='OPER.MINUS'" />
+ <assign location="long_expr" expr="long_expr+'-'" />
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors4.scxml.errors b/tests/auto/parser/data/syntaxErrors4.scxml.errors
new file mode 100644
index 0000000..54e7b4f
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors4.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors4.scxml:12:64: error: Unknown element elsDif
+:/tst_parser/data/syntaxErrors4.scxml:17:8: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors5.scxml b/tests/auto/parser/data/syntaxErrors5.scxml
new file mode 100644
index 0000000..d711cbd
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors5.scxml
@@ -0,0 +1,13 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="wrapper">
+ <transition event="OP.INSERT">
+ <if cond="_event.data.operator == 'OPER.PLUS'">
+ <assign location="long_expr" expr="long_expr+'+'" />
+ <elseif cond="_event.data.operator=='OPER.MINUS'" />
+ <state id="zero1">
+ tion="long_expr" expr="long_expr+'-'" />
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors5.scxml.errors b/tests/auto/parser/data/syntaxErrors5.scxml.errors
new file mode 100644
index 0000000..2e80c0b
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors5.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors5.scxml:8:20: error: Unexpected element state
+:/tst_parser/data/syntaxErrors5.scxml:10:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/syntaxErrors7.scxml b/tests/auto/parser/data/syntaxErrors7.scxml
new file mode 100644
index 0000000..fbe42ce
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors7.scxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="lightImpulseGenerator">
+ <transition event="scheduleNewImpulse">
+ <if cond="In('offState')">
+ <send event="lightImpulse"/>
+ <Elseif cond="In('hurryStateOff')"/>
+ <send event="lightImpulse"/>
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors7.scxml.errors b/tests/auto/parser/data/syntaxErrors7.scxml.errors
new file mode 100644
index 0000000..228a80b
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors7.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/syntaxErrors7.scxml:7:48: error: Unknown element Elseif
diff --git a/tests/auto/parser/data/syntaxErrors9.scxml b/tests/auto/parser/data/syntaxErrors9.scxml
new file mode 100644
index 0000000..5b6ad24
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors9.scxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
+ <state id="workflow">
+ <transition event="updateLights">
+ |if cond="In('jackpotStateOn')">
+ <raise event="turnOnJackpot"/>
+ <else/>
+ <raise event="turnOffJackpot"/>
+ </if>
+ </transition>
+ </state>
+</scxml>
diff --git a/tests/auto/parser/data/syntaxErrors9.scxml.errors b/tests/auto/parser/data/syntaxErrors9.scxml.errors
new file mode 100644
index 0000000..cb5b1e9
--- /dev/null
+++ b/tests/auto/parser/data/syntaxErrors9.scxml.errors
@@ -0,0 +1,2 @@
+:/tst_parser/data/syntaxErrors9.scxml:7:19: error: Unexpected element else
+:/tst_parser/data/syntaxErrors9.scxml:9:17: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/test1.scxml.errors b/tests/auto/parser/data/test1.scxml.errors
new file mode 100644
index 0000000..bb9d53e
--- /dev/null
+++ b/tests/auto/parser/data/test1.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/test1.scxml:34:46: error: unknown state 'b' in target
diff --git a/tests/auto/parser/parser.pro b/tests/auto/parser/parser.pro
index 10deb2d..3a21a71 100644
--- a/tests/auto/parser/parser.pro
+++ b/tests/auto/parser/parser.pro
@@ -11,5 +11,3 @@ RESOURCES += tst_parser.qrc
SOURCES += \
tst_parser.cpp
-
-load(qscxmlc)
diff --git a/tests/auto/parser/tst_parser.cpp b/tests/auto/parser/tst_parser.cpp
index 4c0e19f..202e783 100644
--- a/tests/auto/parser/tst_parser.cpp
+++ b/tests/auto/parser/tst_parser.cpp
@@ -29,7 +29,7 @@
#include <QtTest>
#include <QObject>
#include <QXmlStreamReader>
-#include <QtScxml/qscxmlparser.h>
+#include <QtScxml/qscxmlcompiler.h>
#include <QtScxml/qscxmlstatemachine.h>
Q_DECLARE_METATYPE(QScxmlError);
@@ -46,107 +46,42 @@ private Q_SLOTS:
void tst_Parser::error_data()
{
QTest::addColumn<QString>("scxmlFileName");
- QTest::addColumn<QVector<QScxmlError> >("expectedErrors");
-
- QVector<QScxmlError> errors;
- QString filename;
-
- filename = QLatin1String(":/tst_parser/data/test1.scxml");
- errors.clear();
- errors << QScxmlError(filename, 34, 46,
- QLatin1String("unknown state 'b' in target"));
- QTest::newRow("test1") << filename << errors;
-
- filename = QLatin1String(":/tst_parser/data/namespaces1.scxml");
- errors.clear();
- QTest::newRow("namespaces 1") << filename << errors;
-
- filename = QLatin1String(":/tst_parser/data/ids1.scxml");
- errors.clear();
- QTest::newRow("IDs 1") << filename << errors;
-
- filename = QLatin1String(":/tst_parser/data/ids2.scxml");
- errors.clear();
- errors << QScxmlError(filename, 33, 25,
- QLatin1String("state name 'foo.bar' is not a valid C++ identifier in Qt mode"));
- errors << QScxmlError(filename, 34, 25,
- QLatin1String("state name 'foo-bar' is not a valid C++ identifier in Qt mode"));
- errors << QScxmlError(filename, 36, 19,
- QLatin1String("'1' is not a valid XML ID"));
-
- QTest::newRow("IDs 2") << filename << errors;
-
- filename = QLatin1String(":/tst_parser/data/eventnames.scxml");
- errors.clear();
- errors << QScxmlError(filename, 50, 38,
- QLatin1String("'.invalid' is not a valid event"));
- errors << QScxmlError(filename, 51, 38,
- QLatin1String("'invalid.' is not a valid event"));
- errors << QScxmlError(filename, 39, 36,
- QLatin1String("'.invalid' is not a valid event"));
- errors << QScxmlError(filename, 40, 36,
- QLatin1String("'invalid.' is not a valid event"));
- errors << QScxmlError(filename, 41, 36,
- QLatin1String("'in valid' is not a valid event"));
- QTest::newRow("eventnames") << filename << errors;
-
- filename = QString(":/tst_parser/data/qtmode.scxml");
- errors.clear();
- errors << QScxmlError(filename, 35, 31,
- QLatin1String("event name 'a' collides with a state name 'a' in Qt mode"));
- errors << QScxmlError(filename, 36, 34,
- QLatin1String("event name 'void' is not a valid C++ identifier in Qt mode"));
- errors << QScxmlError(filename, 37, 38,
- QLatin1String("event name 'aChanged' collides with a state name 'a' in Qt mode"));
- errors << QScxmlError(filename, 38, 38,
- QLatin1String("event name 'finished' is not a valid Qt identifier in Qt mode"));
- errors << QScxmlError(filename, 42, 21,
- QLatin1String("state name 'int' is not a valid C++ identifier in Qt mode"));
- errors << QScxmlError(filename, 43, 28,
- QLatin1String("state name 'objectName' is not a valid Qt identifier in Qt mode"));
- errors << QScxmlError(filename, 45, 28,
- QLatin1String("state name 'fooChanged' collides with a state name 'foo' in Qt mode"));
- QTest::newRow("qtmode") << filename << errors;
-
- filename = QString(":/tst_parser/data/scxml1.scxml");
- errors.clear();
- errors << QScxmlError(filename, 32, 36,
- QLatin1String("Unsupported data model 'foo' in scxml"));
- errors << QScxmlError(filename, 34, 30,
- QLatin1String("Unexpected element scxml"));
- QTest::newRow("scxml1") << filename << errors;
-
- filename = QString(":/tst_parser/data/scxml2.scxml");
- errors.clear();
- errors << QScxmlError(filename, 32, 34,
- QLatin1String("Unsupperted binding type 'foo'"));
- QTest::newRow("scxml2") << filename << errors;
+ QTest::addColumn<QString>("errorFileName");
+
+ QDir dir(QLatin1String(":/tst_parser/data/"));
+ const auto dirEntries = dir.entryList();
+ for (const QString &entry : dirEntries) {
+ if (!entry.endsWith(QLatin1String(".errors"))) {
+ QString scxmlFileName = dir.filePath(entry);
+ QTest::newRow(entry.toLatin1().constData())
+ << scxmlFileName << (scxmlFileName + QLatin1String(".errors"));
+ }
+ }
}
void tst_Parser::error()
{
QFETCH(QString, scxmlFileName);
- QFETCH(QVector<QScxmlError>, expectedErrors);
+ QFETCH(QString, errorFileName);
+
+ QFile errorFile(errorFileName);
+ errorFile.open(QIODevice::ReadOnly | QIODevice::Text);
+ const QStringList expectedErrors =
+ QString::fromUtf8(errorFile.readAll()).split('\n', QString::SkipEmptyParts);
QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(scxmlFileName));
QVERIFY(!stateMachine.isNull());
- QVector<QScxmlError> errors = stateMachine->parseErrors();
+ const QVector<QScxmlError> errors = stateMachine->parseErrors();
if (errors.count() != expectedErrors.count()) {
- foreach (const QScxmlError &error, errors) {
+ for (const QScxmlError &error : errors) {
qDebug() << error.toString();
}
}
QCOMPARE(errors.count(), expectedErrors.count());
- for (int i = 0; i < errors.count(); ++i) {
- QScxmlError error = errors.at(i);
- QScxmlError expectedError = expectedErrors.at(i);
- QCOMPARE(error.fileName(), expectedError.fileName());
- QCOMPARE(error.line(), expectedError.line());
- QCOMPARE(error.column(), expectedError.column());
- QCOMPARE(error.description(), expectedError.description());
- }
+ for (int i = 0; i < errors.count(); ++i)
+ QCOMPARE(errors.at(i).toString(), expectedErrors.at(i));
}
QTEST_MAIN(tst_Parser)
diff --git a/tests/auto/parser/tst_parser.qrc b/tests/auto/parser/tst_parser.qrc
index 7323c82..8d133d2 100644
--- a/tests/auto/parser/tst_parser.qrc
+++ b/tests/auto/parser/tst_parser.qrc
@@ -1,12 +1,84 @@
<RCC>
<qresource prefix="/tst_parser">
+ <file>data/badInitial.scxml.errors</file>
+ <file>data/badInitial.scxml</file>
+ <file>data/commentInScript.scxml.errors</file>
+ <file>data/commentInScript.scxml</file>
+ <file>data/empty.scxml.errors</file>
+ <file>data/empty.scxml</file>
+ <file>data/eventnames.scxml.errors</file>
<file>data/eventnames.scxml</file>
+ <file>data/ids1.scxml.errors</file>
<file>data/ids1.scxml</file>
+ <file>data/ids2.scxml.errors</file>
<file>data/ids2.scxml</file>
+ <file>data/invalidContent.scxml.errors</file>
+ <file>data/invalidContent.scxml</file>
+ <file>data/invalidRoot1.scxml.errors</file>
+ <file>data/invalidRoot1.scxml</file>
+ <file>data/invalidRoot2.scxml.errors</file>
+ <file>data/invalidRoot2.scxml</file>
+ <file>data/invalidRoot3.scxml.errors</file>
+ <file>data/invalidRoot3.scxml</file>
+ <file>data/invalidRoot6.scxml.errors</file>
+ <file>data/invalidRoot6.scxml</file>
+ <file>data/invalidstatemachinename.scxml.errors</file>
+ <file>data/invalidstatemachinename.scxml</file>
+ <file>data/invalidUnicode1.scxml.errors</file>
+ <file>data/invalidUnicode1.scxml</file>
+ <file>data/invalidXmlHeader1.scxml.errors</file>
+ <file>data/invalidXmlHeader1.scxml</file>
+ <file>data/invalidXmlHeader2.scxml.errors</file>
+ <file>data/invalidXmlHeader2.scxml</file>
+ <file>data/misplacedinvoke.scxml.errors</file>
+ <file>data/misplacedinvoke.scxml</file>
+ <file>data/namespaces1.scxml.errors</file>
<file>data/namespaces1.scxml</file>
- <file>data/qtmode.scxml</file>
+ <file>data/nestedScxml.scxml.errors</file>
+ <file>data/nestedScxml.scxml</file>
+ <file>data/noContentInInvoke1.scxml.errors</file>
+ <file>data/noContentInInvoke1.scxml</file>
+ <file>data/noContentInInvoke2.scxml.errors</file>
+ <file>data/noContentInInvoke2.scxml</file>
+ <file>data/noContentInInvoke3.scxml.errors</file>
+ <file>data/noContentInInvoke3.scxml</file>
+ <file>data/noContentInInvoke4.scxml.errors</file>
+ <file>data/noContentInInvoke4.scxml</file>
+ <file>data/prematureEndOfDocument1.scxml.errors</file>
+ <file>data/prematureEndOfDocument1.scxml</file>
+ <file>data/prematureEndOfDocument2.scxml.errors</file>
+ <file>data/prematureEndOfDocument2.scxml</file>
+ <file>data/scxml1.scxml.errors</file>
<file>data/scxml1.scxml</file>
+ <file>data/scxml2.scxml.errors</file>
<file>data/scxml2.scxml</file>
+ <file>data/syntaxErrors10.scxml.errors</file>
+ <file>data/syntaxErrors10.scxml</file>
+ <file>data/syntaxErrors11.scxml.errors</file>
+ <file>data/syntaxErrors11.scxml</file>
+ <file>data/syntaxErrors12.scxml.errors</file>
+ <file>data/syntaxErrors12.scxml</file>
+ <file>data/syntaxErrors13.scxml.errors</file>
+ <file>data/syntaxErrors13.scxml</file>
+ <file>data/syntaxErrors14.scxml.errors</file>
+ <file>data/syntaxErrors14.scxml</file>
+ <file>data/syntaxErrors15.scxml.errors</file>
+ <file>data/syntaxErrors15.scxml</file>
+ <file>data/syntaxErrors1.scxml.errors</file>
+ <file>data/syntaxErrors1.scxml</file>
+ <file>data/syntaxErrors2.scxml.errors</file>
+ <file>data/syntaxErrors2.scxml</file>
+ <file>data/syntaxErrors3.scxml.errors</file>
+ <file>data/syntaxErrors3.scxml</file>
+ <file>data/syntaxErrors4.scxml.errors</file>
+ <file>data/syntaxErrors4.scxml</file>
+ <file>data/syntaxErrors5.scxml.errors</file>
+ <file>data/syntaxErrors5.scxml</file>
+ <file>data/syntaxErrors7.scxml.errors</file>
+ <file>data/syntaxErrors7.scxml</file>
+ <file>data/syntaxErrors9.scxml.errors</file>
+ <file>data/syntaxErrors9.scxml</file>
+ <file>data/test1.scxml.errors</file>
<file>data/test1.scxml</file>
</qresource>
</RCC>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf.scxml b/tests/auto/qscxmlc/data/elseWithoutIf.scxml
deleted file mode 100644
index 0674542..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transitifn event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- nameate>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_e:ent.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="shornt.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'." />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- nsi <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assigZ location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf10.scxml b/tests/auto/qscxmlc/data/elseWithoutIf10.scxml
deleted file mode 100644
index 920fd84..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf10.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send>event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf2.scxml b/tests/auto/qscxmlc/data/elseWithoutIf2.scxml
deleted file mode 100644
index 830831f..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf2.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="O" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="o]erand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assige location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf3.scxml b/tests/auto/qscxmlc/data/elseWithoutIf3.scxml
deleted file mode 100644
index beca8f6..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf3.scxml
+++ /dev/null
@@ -1,163 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elsDif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf4.scxml b/tests/auto/qscxmlc/data/elseWithoutIf4.scxml
deleted file mode 100644
index 8e8018d..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf4.scxml
+++ /dev/null
@@ -1,165 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <state id="zero1">
- tion="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf5.scxml b/tests/auto/qscxmlc/data/elseWithoutIf5.scxml
deleted file mode 100644
index 47af4f3..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf5.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- |if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf6.scxml b/tests/auto/qscxmlc/data/elseWithoutIf6.scxml
deleted file mode 100644
index beaf539..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf6.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <Elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf7.scxml b/tests/auto/qscxmlc/data/elseWithoutIf7.scxml
deleted file mode 100644
index 21bfe08..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf7.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- |if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf8.scxml b/tests/auto/qscxmlc/data/elseWithoutIf8.scxml
deleted file mode 100644
index b49bd32..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf8.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- |if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/elseWithoutIf9.scxml b/tests/auto/qscxmlc/data/elseWithoutIf9.scxml
deleted file mode 100644
index b753246..0000000
--- a/tests/auto/qscxmlc/data/elseWithoutIf9.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- |if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidAssign.scxml b/tests/auto/qscxmlc/data/invalidAssign.scxml
deleted file mode 100644
index 3e22993..0000000
--- a/tests/auto/qscxmlc/data/invalidAssign.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <Assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidAssign2.scxml b/tests/auto/qscxmlc/data/invalidAssign2.scxml
deleted file mode 100644
index fb50085..0000000
--- a/tests/auto/qscxmlc/data/invalidAssign2.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr">expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidIf.scxml b/tests/auto/qscxmlc/data/invalidIf.scxml
deleted file mode 100644
index 16788fb..0000000
--- a/tests/auto/qscxmlc/data/invalidIf.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- |if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidIf2.scxml b/tests/auto/qscxmlc/data/invalidIf2.scxml
deleted file mode 100644
index 7239721..0000000
--- a/tests/auto/qscxmlc/data/invalidIf2.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <If cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidInvoke4.scxml b/tests/auto/qscxmlc/data/invalidInvoke4.scxml
deleted file mode 100644
index 5743c85..0000000
--- a/tests/auto/qscxmlc/data/invalidInvoke4.scxml
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- http://www.w3.org/2005/07/scxmlscxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidRoot.scxml b/tests/auto/qscxmlc/data/invalidRoot.scxml
deleted file mode 100644
index 8f23220..0000000
--- a/tests/auto/qscxmlc/data/invalidRoot.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" ?>
-<Scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidRoot3.scxml b/tests/auto/qscxmlc/data/invalidRoot3.scxml
deleted file mode 100644
index f0f4a79..0000000
--- a/tests/auto/qscxmlc/data/invalidRoot3.scxml
+++ /dev/null
Binary files differ
diff --git a/tests/auto/qscxmlc/data/invalidRoot4.scxml b/tests/auto/qscxmlc/data/invalidRoot4.scxml
deleted file mode 100644
index 2e2b6f0..0000000
--- a/tests/auto/qscxmlc/data/invalidRoot4.scxml
+++ /dev/null
@@ -1,374 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: no -->
-<Scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send type="qt:signal" event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- <else/>
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidScxml.scxml b/tests/auto/qscxmlc/data/invalidScxml.scxml
deleted file mode 100644
index 9c3ba01..0000000
--- a/tests/auto/qscxmlc/data/invalidScxml.scxml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<Scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content>
- <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/invalidXmlHeader.scxml b/tests/auto/qscxmlc/data/invalidXmlHeader.scxml
deleted file mode 100644
index 84a6c35..0000000
--- a/tests/auto/qscxmlc/data/invalidXmlHeader.scxml
+++ /dev/null
Binary files differ
diff --git a/tests/auto/qscxmlc/data/invalidXmlHeader2.scxml b/tests/auto/qscxmlc/data/invalidXmlHeader2.scxml
deleted file mode 100644
index d2b0a3b..0000000
--- a/tests/auto/qscxmlc/data/invalidXmlHeader2.scxml
+++ /dev/null
@@ -1,164 +0,0 @@
-<ry>l version="1.0" ?>
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- initial="wrapper" datamodel="ecmascript" name="CalculatorStateMachine">
- <datamodel>
- <data id="long_expr" />
- <data id="short_expr" />
- <data id="res" />
- </datamodel>
- <state id="wrapper" initial="on">
- <state id="on" initial="ready">
- <onentry>
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <state id="ready" initial="begin">
- <state id="begin">
- <transition event="OPER.MINUS" target="negated1" />
- <onentry>
- <assign location="long_expr" expr="''" />
- <assign location="short_expr" expr="0" />
- <assign location="res" expr="0" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="result">
- </state>
- <transition event="OPER" target="opEntered" />
- <transition event="DIGIT.0" target="zero1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="DIGIT" target="int1">
- <assign location="short_expr" expr="''" />
- </transition>
- <transition event="POINT" target="frac1">
- <assign location="short_expr" expr="''" />
- </transition>
- </state>
- <state id="negated1">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero1" />
- <transition event="DIGIT" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="operand1">
- <state id="zero1">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int1" />
- <transition event="POINT" target="frac1" />
- </state>
- <state id="int1">
- <transition event="POINT" target="frac1" />
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- </state>
- <state id="frac1">
- <onentry>
- <assign location="short_expr" expr="short_expr+'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered" />
- </state>
- <state id="opEntered">
- <transition event="OPER.MINUS" target="negated2" />
- <transition event="POINT" target="frac2" />
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <onentry>
- <raise event="CALC.SUB" />
- <send target="#_internal" event="OP.INSERT">
- <param name="operator" expr="_event.name" />
- </send>
- </onentry>
- </state>
- <state id="negated2">
- <onentry>
- <assign location="short_expr" expr="'-'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT.0" target="zero2" />
- <transition event="DIGIT" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="operand2">
- <state id="zero2">
- <transition event="DIGIT" cond="_event.name != 'DIGIT.0'" target="int2" />
- <transition event="POINT" target="frac2" />
- </state>
- <state id="int2">
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <onentry>
- <assign location="short_expr" expr="short_expr+_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="POINT" target="frac2" />
- </state>
- <state id="frac2">
- <onentry>
- <assign location="short_expr" expr="short_expr +'.'" />
- <send event="DISPLAY.UPDATE" />
- </onentry>
- <transition event="DIGIT">
- <assign location="short_expr" expr="short_expr +_event.name.substr(_event.name.lastIndexOf('.')+1)" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- </state>
- <transition event="OPER" target="opEntered">
- <raise event="CALC.SUB" />
- <raise event="OP.INSERT" />
- </transition>
- <transition event="EQUALS" target="result">
- <raise event="CALC.SUB" />
- <raise event="CALC.DO" />
- </transition>
- </state>
- <transition event="C" target="on" />
- </state>
- <transition event="CALC.DO">
- <assign location="short_expr" expr="''+ res" />
- <assign location="long_expr" expr="''" />
- <assign location="res" expr="0" />
- </transition>
- <transition event="CALC.SUB">
- <if cond="short_expr!=''">
- <assign location="long_expr" expr="long_expr+'('+short_expr+')'" />
- </if>
- <assign location="res" expr="eval(long_expr)" />
- <assign location="short_expr" expr="''" />
- <send event="DISPLAY.UPDATE" />
- </transition>
- <transition event="DISPLAY.UPDATE">
- <log label="'result'" expr="short_expr==''?res:short_expr" />
- <send type="qt:signal" event="updateDisplay">
- <param name="display" expr="short_expr==''?res:short_expr"/>
- </send>
- </transition>
- <transition event="OP.INSERT">
- <log expr="_event.data.operator" />
- <if cond="_event.data.operator == 'OPER.PLUS'">
- <assign location="long_expr" expr="long_expr+'+'" />
- <elseif cond="_event.data.operator=='OPER.MINUS'" />
- <assign location="long_expr" expr="long_expr+'-'" />
- <elseif cond="_event.data.operator=='OPER.STAR'" />
- <assign location="long_expr" expr="long_expr+'*'" />
- <elseif cond="_event.data.operator=='OPER.DIV'" />
- <assign location="long_expr" expr="long_expr+'/'" />
- </if>
- </transition>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/noContentInInvoke4.scxml b/tests/auto/qscxmlc/data/noContentInInvoke4.scxml
deleted file mode 100644
index 49eaa54..0000000
--- a/tests/auto/qscxmlc/data/noContentInInvoke4.scxml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplacE"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="s* LIMITomewhere">
- <invoke type="http://www.w$.org/TR/scxml/">
-  <content>
- <scxml name="anywhere" version="1.0">
- <sta e id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxm8>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/prematureEndOfDocument.scxml b/tests/auto/qscxmlc/data/prematureEndOfDocument.scxml
deleted file mode 100644
index 73e0138..0000000
--- a/tests/auto/qscxmlc/data/prematureEndOfDocument.scxml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: yes m->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content>
- <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/prematureEndOfDocument2.scxml b/tests/auto/qscxmlc/data/prematureEndOfDocument2.scxml
deleted file mode 100644
index ce56def..0000000
--- a/tests/auto/qscxmlc/data/prematureEndOfDocument2.scxml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content>
-<!-- <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/data/wrongRoot.scxml b/tests/auto/qscxmlc/data/wrongRoot.scxml
deleted file mode 100644
index ce225b0..0000000
--- a/tests/auto/qscxmlc/data/wrongRoot.scxml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<!-- enable-qt-mode: yes -->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content>
- |scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/tests/auto/qscxmlc/qscxmlc.pro b/tests/auto/qscxmlc/qscxmlc.pro
deleted file mode 100644
index 2024920..0000000
--- a/tests/auto/qscxmlc/qscxmlc.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT = core testlib scxml-private
-CONFIG += testcase console c++11
-CONFIG -= app_bundle
-
-TARGET = tst_qscxmlc
-TEMPLATE = app
-
-RESOURCES += tst_qscxmlc.qrc
-
-include(../../../tools/qscxmlc/qscxmlc.pri)
-
-INCLUDEPATH += ../../../tools/qscxmlc/
-
-SOURCES += \
- tst_qscxmlc.cpp
diff --git a/tests/auto/qscxmlc/tst_qscxmlc.qrc b/tests/auto/qscxmlc/tst_qscxmlc.qrc
deleted file mode 100644
index a86c786..0000000
--- a/tests/auto/qscxmlc/tst_qscxmlc.qrc
+++ /dev/null
@@ -1,43 +0,0 @@
-<RCC>
- <qresource prefix="/tst_qscxmlc">
- <file>data/commentInScript.scxml</file>
- <file>data/empty.scxml</file>
- <file>data/misplacedinvoke.scxml</file>
- <file>data/invalidRoot.scxml</file>
- <file>data/invalidIf.scxml</file>
- <file>data/invalidIf2.scxml</file>
- <file>data/invalidAssign.scxml</file>
- <file>data/invalidAssign2.scxml</file>
- <file>data/invalidXmlHeader.scxml</file>
- <file>data/invalidXmlHeader2.scxml</file>
- <file>data/elseWithoutIf.scxml</file>
- <file>data/elseWithoutIf2.scxml</file>
- <file>data/elseWithoutIf3.scxml</file>
- <file>data/invalidstatemachinename.scxml</file>
- <file>data/invalidScxml.scxml</file>
- <file>data/prematureEndOfDocument.scxml</file>
- <file>data/elseWithoutIf4.scxml</file>
- <file>data/badInitial.scxml</file>
- <file>data/elseWithoutIf5.scxml</file>
- <file>data/elseWithoutIf6.scxml</file>
- <file>data/elseWithoutIf7.scxml</file>
- <file>data/elseWithoutIf8.scxml</file>
- <file>data/elseWithoutIf9.scxml</file>
- <file>data/elseWithoutIf10.scxml</file>
- <file>data/invalidContent.scxml</file>
- <file>data/invalidInvoke.scxml</file>
- <file>data/invalidInvoke2.scxml</file>
- <file>data/invalidInvoke3.scxml</file>
- <file>data/invalidInvoke4.scxml</file>
- <file>data/invalidRoot2.scxml</file>
- <file>data/invalidRoot3.scxml</file>
- <file>data/invalidRoot4.scxml</file>
- <file>data/noContentInInvoke.scxml</file>
- <file>data/noContentInInvoke2.scxml</file>
- <file>data/noContentInInvoke3.scxml</file>
- <file>data/noContentInInvoke4.scxml</file>
- <file>data/prematureEndOfDocument2.scxml</file>
- <file>data/wrongRoot.scxml</file>
- <file>data/nestedScxml.scxml</file>
- </qresource>
-</RCC>
diff --git a/tests/auto/scion/scion.pro b/tests/auto/scion/scion.pro
index eccc02d..6e1f7f4 100644
--- a/tests/auto/scion/scion.pro
+++ b/tests/auto/scion/scion.pro
@@ -28,7 +28,6 @@ defineReplace(nameTheClass) {
qtPrepareTool(QMAKE_QSCXMLC, qscxmlc)
win32 {
- QMAKE_QSCXMLC += --no-c++11
msvc: QMAKE_CXXFLAGS += /bigobj
}
@@ -52,6 +51,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 \
@@ -101,5 +101,3 @@ write_file("scxml/scion.h", contents)|error("Aborting.")
contents = '<!DOCTYPE RCC><RCC version=\"1.0\">' '<qresource>' $$qrc '</qresource></RCC>'
write_file("scion.qrc", contents)|error("Aborting.")
-
-load(qscxmlc)
diff --git a/tests/auto/scion/tst_scion.cpp b/tests/auto/scion/tst_scion.cpp
index 695d1ba..a068005 100644
--- a/tests/auto/scion/tst_scion.cpp
+++ b/tests/auto/scion/tst_scion.cpp
@@ -30,9 +30,8 @@
#include <QCoreApplication>
#include <QJsonDocument>
-#include <QtScxml/qscxmlparser.h>
+#include <QtScxml/qscxmlcompiler.h>
#include <QtScxml/qscxmlecmascriptdatamodel.h>
-#include <QtScxml/qscxmlnulldatamodel.h>
#include <functional>
@@ -55,8 +54,6 @@ static QSet<QString> testFailOnRun = QSet<QString>()
<< QLatin1String("w3c-ecma/test178.txml")
// We do not support the optional basic http event i/o processor.
<< QLatin1String("w3c-ecma/test201.txml")
- << QLatin1String("w3c-ecma/test364.txml") // initial attribute on <state>
- << QLatin1String("w3c-ecma/test388.txml") // Qt refuses to set an initial state to a "deep" state
<< QLatin1String("w3c-ecma/test230.txml")
<< QLatin1String("w3c-ecma/test250.txml")
<< QLatin1String("w3c-ecma/test307.txml")
@@ -67,8 +64,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")
;
@@ -91,45 +86,49 @@ public:
}
};
-class DynamicLoader: public QScxmlParser::Loader
+class DynamicLoader: public QScxmlCompiler::Loader
{
public:
- DynamicLoader(QScxmlParser *parser);
- QByteArray load(const QString &name, const QString &baseDir, bool *ok) Q_DECL_OVERRIDE Q_DECL_FINAL;
+ DynamicLoader();
+ QByteArray load(const QString &name,
+ const QString &baseDir,
+ QStringList *errors) Q_DECL_OVERRIDE Q_DECL_FINAL;
};
-DynamicLoader::DynamicLoader(QScxmlParser *parser)
- : Loader(parser)
+DynamicLoader::DynamicLoader()
+ : Loader()
{}
-QByteArray DynamicLoader::load(const QString &name, const QString &baseDir, bool *ok)
+QByteArray DynamicLoader::load(const QString &name,
+ const QString &baseDir,
+ QStringList *errors)
{
- Q_ASSERT(ok != nullptr);
-
- *ok = false;
+ QStringList errs;
+ QByteArray contents;
QUrl url(name);
if (!url.isLocalFile() && !url.isRelative())
- parser()->addError(QStringLiteral("src attribute is not a local file (%1)").arg(name));
+ errs << QStringLiteral("src attribute is not a local file (%1)").arg(name);
QFileInfo fInfo = url.isLocalFile() ? url.toLocalFile() : name;
if (fInfo.isRelative())
fInfo = QFileInfo(QDir(baseDir).filePath(fInfo.filePath()));
fInfo = QFileInfo(QLatin1String(":/") + fInfo.filePath()); // take it from resources
if (!fInfo.exists()) {
- parser()->addError(QStringLiteral("src attribute resolves to non existing file (%1)").arg(fInfo.absoluteFilePath()));
+ errs << QStringLiteral("src attribute resolves to non existing file (%1)")
+ .arg(fInfo.absoluteFilePath());
} else {
QFile f(fInfo.absoluteFilePath());
- if (f.open(QFile::ReadOnly)) {
- *ok = true;
- QByteArray array = f.readAll();
- return array;
- } else {
- parser()->addError(QStringLiteral("Failure opening file %1: %2")
- .arg(fInfo.absoluteFilePath(), f.errorString()));
- }
+ if (f.open(QFile::ReadOnly))
+ contents = f.readAll();
+ else
+ errs << QStringLiteral("Failure opening file %1: %2")
+ .arg(fInfo.absoluteFilePath(), f.errorString());
}
- return QByteArray();
+ if (errors)
+ *errors = errs;
+
+ return contents;
}
@@ -202,18 +201,16 @@ void TestScion::dynamic()
QFile scxmlFile(QLatin1String(":/") + scxml);
QVERIFY(scxmlFile.open(QIODevice::ReadOnly));
QXmlStreamReader xmlReader(&scxmlFile);
- QScxmlParser parser(&xmlReader);
- parser.setFileName(scxml);
- DynamicLoader loader(&parser);
- parser.setLoader(&loader);
- parser.parse();
- QVERIFY(parser.errors().isEmpty());
+ QScxmlCompiler compiler(&xmlReader);
+ compiler.setFileName(scxml);
+ DynamicLoader loader;
+ compiler.setLoader(&loader);
+ QScopedPointer<QScxmlStateMachine> stateMachine(compiler.compile());
+ QVERIFY(compiler.errors().isEmpty());
scxmlFile.close();
- QScopedPointer<QScxmlStateMachine> stateMachine(parser.instantiateStateMachine());
QVERIFY(stateMachine != Q_NULLPTR);
-
- parser.instantiateDataModel(stateMachine.data());
+ stateMachine->setLoader(&loader);
const bool runResult = runTest(stateMachine.data(), testDescription.object());
if (runResult == false && testStatus == TestFailsOnRun)
@@ -258,6 +255,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)
@@ -382,7 +381,10 @@ bool TestScion::runTest(QScxmlStateMachine *stateMachine, const QJsonObject &tes
return playEvents(stateMachine, testDescription);
} else {
// Wait for all events (delayed or otherwise) to propagate.
- finishedSpy.fastWait(); // Some tests don't have a final state, so don't check for the result.
+ if (stateMachine->isRunning()) {
+ finishedSpy.fastWait(); // Some tests don't have a final state, so don't check for the
+ // result
+ }
return verifyStates(stateMachine, testDescription, QLatin1String("initialConfiguration"), 0);
}
}
diff --git a/tests/auto/statemachine/eventoccurred.scxml b/tests/auto/statemachine/eventoccurred.scxml
index 87aaf41..c77da54 100644
--- a/tests/auto/statemachine/eventoccurred.scxml
+++ b/tests/auto/statemachine/eventoccurred.scxml
@@ -35,19 +35,19 @@
<onentry>
<raise event="internalEvent1"/>
<send event="internalEvent2"/>
- <send type="qt:signal" event="externalEvent"/>
+ <send event="externalEvent"/>
<send event="timeout" delay="1s"/>
</onentry>
<transition event="timeout" target="final"/>
</state>
<final id="final"/>
- </state>
- <!--
- The done.state.* events are internal, so expose them to the spy too by re-sending them as
- external events:
- -->
- <transition event="done.state.*" cond="_event.type === 'internal'">
- <send eventexpr="_event.name"/>
- </transition>
+ <!--
+ The done.state.* events are internal, so expose them to the spy too by re-sending them as
+ external events:
+ -->
+ <transition event="done.state.*" cond="_event.type === 'internal'">
+ <send eventexpr="_event.name"/>
+ </transition>
+ </state>
</scxml>
diff --git a/tests/auto/statemachine/statemachine.pro b/tests/auto/statemachine/statemachine.pro
index c96ce21..0e4de1a 100644
--- a/tests/auto/statemachine/statemachine.pro
+++ b/tests/auto/statemachine/statemachine.pro
@@ -1,4 +1,4 @@
-QT = core gui qml testlib scxml
+QT = core gui qml testlib scxml-private
CONFIG += testcase
TARGET = tst_statemachine
@@ -11,5 +11,3 @@ RESOURCES += tst_statemachine.qrc
SOURCES += \
tst_statemachine.cpp
-
-load(qscxmlc)
diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp
index fc20118..f09ad42 100644
--- a/tests/auto/statemachine/tst_statemachine.cpp
+++ b/tests/auto/statemachine/tst_statemachine.cpp
@@ -29,8 +29,9 @@
#include <QtTest>
#include <QObject>
#include <QXmlStreamReader>
-#include <QtScxml/qscxmlparser.h>
+#include <QtScxml/qscxmlcompiler.h>
#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/private/qscxmlstatemachine_p.h>
Q_DECLARE_METATYPE(QScxmlError);
@@ -45,7 +46,8 @@ private Q_SLOTS:
void stateNames();
void activeStateNames_data();
void activeStateNames();
- void connectToFinal();
+ void connections();
+ void onExit();
void eventOccurred();
void doneDotStateEvent();
@@ -62,18 +64,18 @@ void tst_StateMachine::stateNames_data()
<< (QStringList() << QString("a1") << QString("a2") << QString("final"));
QTest::newRow("stateNames-notCompressed") << QString(":/tst_statemachine/statenames.scxml")
<< false
- << (QStringList() << QString("a") << QString("a1") << QString("a2") << QString("b") << QString("final") << QString("top"));
+ << (QStringList() << QString("top") << QString("a") << QString("a1") << QString("a2") << QString("b") << QString("final"));
QTest::newRow("stateNamesNested-compressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< true
<< (QStringList() << QString("a") << QString("b"));
QTest::newRow("stateNamesNested-notCompressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< false
- << (QStringList() << QString("a") << QString("b") << QString("super_top"));
+ << (QStringList() << QString("super_top") << QString("a") << QString("b"));
QTest::newRow("ids1") << QString(":/tst_statemachine/ids1.scxml")
<< false
- << (QStringList() << QString("_") << QString("foo-bar")
- << QString("foo.bar") << QString("foo_bar"));
+ << (QStringList() << QString("foo.bar") << QString("foo-bar")
+ << QString("foo_bar") << QString("_"));
}
void tst_StateMachine::stateNames()
@@ -100,13 +102,13 @@ void tst_StateMachine::activeStateNames_data()
<< (QStringList() << QString("a1") << QString("final"));
QTest::newRow("stateNames-notCompressed") << QString(":/tst_statemachine/statenames.scxml")
<< false
- << (QStringList() << QString("a") << QString("a1") << QString("b") << QString("final") << QString("top"));
+ << (QStringList() << QString("top") << QString("a") << QString("a1") << QString("b") << QString("final"));
QTest::newRow("stateNamesNested-compressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< true
- << (QStringList() << QString("a")<< QString("b"));
+ << (QStringList() << QString("a") << QString("b"));
QTest::newRow("stateNamesNested-notCompressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< false
- << (QStringList() << QString("a") << QString("b") << QString("super_top"));
+ << (QStringList() << QString("super_top") << QString("a") << QString("b"));
}
void tst_StateMachine::activeStateNames()
@@ -127,13 +129,159 @@ void tst_StateMachine::activeStateNames()
QCOMPARE(stateMachine->activeStateNames(compressed), expectedStates);
}
-void tst_StateMachine::connectToFinal()
+class Receiver : public QObject {
+ Q_OBJECT
+public slots:
+ void a(bool enabled)
+ {
+ aReached = aReached || enabled;
+ }
+
+ void b(bool enabled)
+ {
+ bReached = bReached || enabled;
+ }
+
+ void aEnter()
+ {
+ aEntered = true;
+ }
+
+ void aExit()
+ {
+ aExited = true;
+ }
+
+public:
+ bool aReached = false;
+ bool bReached = false;
+ bool aEntered = false;
+ bool aExited = false;
+};
+
+void tst_StateMachine::connections()
{
- QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(QString(":/tst_statemachine/statenames.scxml")));
+ QScopedPointer<QScxmlStateMachine> stateMachine(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachine/statenames.scxml")));
QVERIFY(!stateMachine.isNull());
+ Receiver receiver;
+
+ bool a1Reached = false;
+ bool finalReached = false;
+ QMetaObject::Connection a = stateMachine->connectToState("a", &receiver, &Receiver::a);
+ QVERIFY(a);
+ QMetaObject::Connection b = stateMachine->connectToState("b", &receiver, SLOT(b(bool)));
+ QVERIFY(b);
+ QMetaObject::Connection a1 = stateMachine->connectToState("a1", &receiver,
+ [&a1Reached](bool enabled) {
+ a1Reached = a1Reached || enabled;
+ });
+ QVERIFY(a1);
+ QMetaObject::Connection final = stateMachine->connectToState("final",
+ [&finalReached](bool enabled) {
+ finalReached = finalReached || enabled;
+ });
+ QVERIFY(final);
+
+ bool a1Entered = false;
+ bool a1Exited = false;
+ bool finalEntered = false;
+ bool finalExited = false;
+ typedef QScxmlStateMachine QXSM;
+
+ QMetaObject::Connection aEntry = stateMachine->connectToState(
+ "a", QXSM::onEntry(&receiver, &Receiver::aEnter));
+ QVERIFY(aEntry);
+ QMetaObject::Connection aExit = stateMachine->connectToState(
+ "a", QXSM::onExit(&receiver, &Receiver::aExit));
+ QVERIFY(aExit);
+ QMetaObject::Connection a1Entry = stateMachine->connectToState("a1", &receiver,
+ QXSM::onEntry([&a1Entered]() {
+ a1Entered = true;
+ }));
+ QVERIFY(a1Entry);
+ QMetaObject::Connection a1Exit = stateMachine->connectToState("a1", &receiver,
+ QXSM::onExit([&a1Exited]() {
+ a1Exited = true;
+ }));
+ QVERIFY(a1Exit);
+
+ QMetaObject::Connection finalEntry = stateMachine->connectToState(
+ "final", QXSM::onEntry([&finalEntered]() {
+ finalEntered = true;
+ }));
+ QVERIFY(finalEntry);
+
+ QMetaObject::Connection finalExit = stateMachine->connectToState(
+ "final", QXSM::onExit([&finalExited]() {
+ finalExited = true;
+ }));
+ QVERIFY(finalExit);
+
+ stateMachine->start();
+
+ QTRY_VERIFY(a1Reached);
+ QTRY_VERIFY(finalReached);
+ QTRY_VERIFY(receiver.aReached);
+ QTRY_VERIFY(receiver.bReached);
+
+ QVERIFY(disconnect(a));
+ QVERIFY(disconnect(b));
+ QVERIFY(disconnect(a1));
+ QVERIFY(disconnect(final));
+
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ QVERIFY(receiver.aEntered);
+ QVERIFY(!receiver.aExited);
+ QVERIFY(a1Entered);
+ QVERIFY(!a1Exited);
+ QVERIFY(finalEntered);
+ QVERIFY(!finalExited);
+
+ QVERIFY(disconnect(aEntry));
+ QVERIFY(disconnect(aExit));
+ QVERIFY(disconnect(a1Entry));
+ QVERIFY(disconnect(a1Exit));
+ QVERIFY(disconnect(finalEntry));
+ QVERIFY(disconnect(finalExit));
+#endif
+}
- QState dummy;
- QVERIFY(stateMachine->connectToState(QString("final"), &dummy, SLOT(deleteLater())));
+void tst_StateMachine::onExit()
+{
+#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304
+ // Test onExit being actually called
+
+ typedef QScxmlStateMachine QXSM;
+ QScopedPointer<QXSM> stateMachine(QXSM::fromFile(QString(":/tst_statemachine/eventoccurred.scxml")));
+
+ Receiver receiver;
+ bool aExited1 = false;
+
+ stateMachine->connectToState("a", QXSM::onExit([&aExited1]() { aExited1 = true; }));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver, &Receiver::aExit));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver, "aEnter"));
+ {
+ // Should not crash
+ Receiver receiver2;
+ stateMachine->connectToState("a", QXSM::onEntry(&receiver2, &Receiver::aEnter));
+ stateMachine->connectToState("a", QXSM::onEntry(&receiver2, "aExit"));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver2, &Receiver::aExit));
+ stateMachine->connectToState("a", QXSM::onExit(&receiver2, "aEnter"));
+ }
+
+ stateMachine->start();
+ QTRY_VERIFY(receiver.aEntered);
+ QTRY_VERIFY(receiver.aExited);
+ QTRY_VERIFY(aExited1);
+#endif
+}
+
+bool hasChildEventRouters(QScxmlStateMachine *stateMachine)
+{
+ // Cast to QObject, to avoid ambigous "children" member.
+ const QObject &parentRouter = QScxmlStateMachinePrivate::get(stateMachine)->m_router;
+ return !parentRouter.children().isEmpty();
}
void tst_StateMachine::eventOccurred()
@@ -143,22 +291,63 @@ void tst_StateMachine::eventOccurred()
qRegisterMetaType<QScxmlEvent>();
QSignalSpy finishedSpy(stateMachine.data(), SIGNAL(finished()));
- QSignalSpy eventOccurredSpy(stateMachine.data(), SIGNAL(eventOccurred(QScxmlEvent)));
- QSignalSpy externalEventOccurredSpy(stateMachine.data(), SIGNAL(externalEventOccurred(QScxmlEvent)));
+
+ int events = 0;
+ auto con1 = stateMachine->connectToEvent("internalEvent2", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 1);
+ QCOMPARE(event.name(), QString("internalEvent2"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con1);
+
+ auto con2 = stateMachine->connectToEvent("externalEvent", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 2);
+ QCOMPARE(event.name(), QString("externalEvent"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con2);
+
+ auto con3 = stateMachine->connectToEvent("timeout", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 3);
+ QCOMPARE(event.name(), QString("timeout"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con3);
+
+ auto con4 = stateMachine->connectToEvent("done.*", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 4);
+ QCOMPARE(event.name(), QString("done.state.top"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con4);
+
+ auto con5 = stateMachine->connectToEvent("done.state", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 5);
+ QCOMPARE(event.name(), QString("done.state.top"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con5);
+
+ auto con6 = stateMachine->connectToEvent("done.state.top", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 6);
+ QCOMPARE(event.name(), QString("done.state.top"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con6);
stateMachine->start();
finishedSpy.wait(5000);
+ QCOMPARE(events, 6);
- QCOMPARE(eventOccurredSpy.count(), 4);
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(0).at(0)).name(), QLatin1String("internalEvent2"));
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(1).at(0)).name(), QLatin1String("externalEvent"));
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(2).at(0)).name(), QLatin1String("timeout"));
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(3).at(0)).name(), QLatin1String("done.state.top"));
-
+ QVERIFY(disconnect(con1));
+ QVERIFY(disconnect(con2));
+ QVERIFY(disconnect(con3));
+ QVERIFY(disconnect(con4));
+ QVERIFY(disconnect(con5));
+ QVERIFY(disconnect(con6));
- QCOMPARE(externalEventOccurredSpy.count(), 1);
- QCOMPARE(qvariant_cast<QScxmlEvent>(externalEventOccurredSpy.at(0).at(0)).name(), QLatin1String("externalEvent"));
+ QTRY_VERIFY(!hasChildEventRouters(stateMachine.data()));
}
void tst_StateMachine::doneDotStateEvent()
diff --git a/tests/auto/qscxmlc/tst_qscxmlc.cpp b/tests/auto/statemachineinfo/statemachine.scxml
index 85c2932..94f06b1 100644
--- a/tests/auto/qscxmlc/tst_qscxmlc.cpp
+++ b/tests/auto/statemachineinfo/statemachine.scxml
@@ -1,3 +1,5 @@
+<?xml version="1.0" ?>
+<!--
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -25,38 +27,19 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-
-#include <QtTest>
-#include <QObject>
-#include <QCoreApplication>
-#include "qscxmlc.h"
-
-class tst_Qscxmlc: public QObject
-{
- Q_OBJECT
-
-private Q_SLOTS:
- void parsing_data();
- void parsing();
-};
-
-void tst_Qscxmlc::parsing_data()
-{
- QTest::addColumn<QString>("scxmlFileName");
- QDir dir(QLatin1String(":/tst_qscxmlc/data/"));
- foreach (const QString &entry, dir.entryList()) {
- QTest::newRow(entry.toLatin1().constData()) << dir.filePath(entry);
- }
-}
-
-void tst_Qscxmlc::parsing()
-{
- QFETCH(QString, scxmlFileName);
- QVERIFY(run(QStringList() << QLatin1String("qcsxmlc") << scxmlFileName));
-}
-
-QTEST_MAIN(tst_Qscxmlc)
-
-#include "tst_qscxmlc.moc"
-
-
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="InfoTest">
+ <transition/>
+ <state>
+ <transition target="next" event="step"/>
+ </state>
+ <parallel id="next">
+ <state id="a">
+ <transition target="theEnd" event="step"/>
+ </state>
+ <state id="b">
+ <transition target="theEnd" event="step" type="internal"/>
+ </state>
+ </parallel>
+ <final id="theEnd"/>
+</scxml>
diff --git a/tests/auto/statemachineinfo/statemachineinfo.pro b/tests/auto/statemachineinfo/statemachineinfo.pro
new file mode 100644
index 0000000..f8d5b5d
--- /dev/null
+++ b/tests/auto/statemachineinfo/statemachineinfo.pro
@@ -0,0 +1,13 @@
+QT = core gui qml testlib scxml-private
+CONFIG += testcase
+
+TARGET = tst_statemachineinfo
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+RESOURCES += tst_statemachineinfo.qrc
+
+SOURCES += \
+ tst_statemachineinfo.cpp
diff --git a/tests/auto/statemachineinfo/tst_statemachineinfo.cpp b/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
new file mode 100644
index 0000000..b1ecfe7
--- /dev/null
+++ b/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtScxml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
+
+Q_DECLARE_METATYPE(QScxmlError);
+
+class tst_StateMachineInfo: public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void checkInfo();
+};
+
+class Recorder: public QObject
+{
+ Q_OBJECT
+
+public:
+ void clear()
+ {
+ enterCount = 0;
+ entered.clear();
+ exitCount = 0;
+ exited.clear();
+ transitionTriggerCount = 0;
+ transitions.clear();
+ macroStepDone = false;
+ }
+
+public slots:
+ void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states)
+ { entered = states; ++enterCount; }
+
+ void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states)
+ { exited = states; ++exitCount; }
+
+ void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions)
+ { this->transitions = transitions; ++transitionTriggerCount; }
+
+ void reachedStableState()
+ { macroStepDone = true; }
+
+ bool finishMacroStep() const
+ {
+ for (int i = 0; i < 100; ++i) {
+ if (!macroStepDone)
+ QCoreApplication::processEvents();
+ }
+
+ return macroStepDone;
+ }
+
+public:
+ int enterCount = 0;
+ QVector<QScxmlStateMachineInfo::StateId> entered;
+ int exitCount = 0;
+ QVector<QScxmlStateMachineInfo::StateId> exited;
+ int transitionTriggerCount = 0;
+ QVector<QScxmlStateMachineInfo::TransitionId> transitions;
+ bool macroStepDone = false;
+};
+
+void tst_StateMachineInfo::checkInfo()
+{
+ QScopedPointer<QScxmlStateMachine> stateMachine(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachineinfo/statemachine.scxml")));
+ QVERIFY(!stateMachine.isNull());
+ QVERIFY(stateMachine->parseErrors().isEmpty());
+ auto info = new QScxmlStateMachineInfo(stateMachine.data());
+
+ const QString machineName = QLatin1String("InfoTest");
+ QCOMPARE(stateMachine->name(), machineName);
+
+ auto states = info->allStates();
+ QCOMPARE(states.size(), 5);
+ QCOMPARE(info->stateName(states.at(0)), QLatin1String(""));
+ QCOMPARE(info->stateName(states.at(1)), QLatin1String("next"));
+ QCOMPARE(info->stateName(states.at(2)), QLatin1String("a"));
+ QCOMPARE(info->stateName(states.at(3)), QLatin1String("b"));
+ QCOMPARE(info->stateName(states.at(4)), QLatin1String("theEnd"));
+
+ QCOMPARE(info->stateParent(QScxmlStateMachineInfo::InvalidState),
+ static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
+ QCOMPARE(info->stateParent(states.at(0)), static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
+ QCOMPARE(info->stateParent(states.at(1)), static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
+ QCOMPARE(info->stateParent(states.at(2)), 1);
+ QCOMPARE(info->stateParent(states.at(3)), 1);
+ QCOMPARE(info->stateParent(states.at(4)), static_cast<int>(QScxmlStateMachineInfo::InvalidStateId));
+
+ QCOMPARE(info->stateType(states.at(0)), QScxmlStateMachineInfo::NormalState);
+ QCOMPARE(info->stateType(states.at(1)), QScxmlStateMachineInfo::ParallelState);
+ QCOMPARE(info->stateType(states.at(2)), QScxmlStateMachineInfo::NormalState);
+ QCOMPARE(info->stateType(states.at(3)), QScxmlStateMachineInfo::NormalState);
+ QCOMPARE(info->stateType(states.at(4)), QScxmlStateMachineInfo::FinalState);
+
+ QCOMPARE(info->stateChildren(QScxmlStateMachineInfo::InvalidStateId),
+ QVector<int>() << 0 << 1 << 4);
+ QCOMPARE(info->stateChildren(states.at(0)), QVector<int>());
+ QCOMPARE(info->stateChildren(states.at(1)), QVector<int>() << 2 << 3);
+ QCOMPARE(info->stateChildren(states.at(2)), QVector<int>());
+ QCOMPARE(info->stateChildren(states.at(3)), QVector<int>());
+ QCOMPARE(info->stateChildren(states.at(4)), QVector<int>());
+
+ QCOMPARE(info->initialTransition(QScxmlStateMachineInfo::InvalidStateId), 4);
+ QCOMPARE(info->initialTransition(states.at(0)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
+ QCOMPARE(info->initialTransition(states.at(1)), 5);
+ QCOMPARE(info->initialTransition(states.at(2)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
+ QCOMPARE(info->initialTransition(states.at(3)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
+ QCOMPARE(info->initialTransition(states.at(4)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
+
+ auto transitions = info->allTransitions();
+ QCOMPARE(transitions.size(), 6);
+
+ // targetless transition on top level
+ QCOMPARE(info->transitionType(transitions.at(0)), QScxmlStateMachineInfo::ExternalTransition);
+ QCOMPARE(info->stateType(info->transitionSource(transitions.at(0))),
+ QScxmlStateMachineInfo::InvalidState);
+ QCOMPARE(info->transitionTargets(transitions.at(0)).size(), 0);
+ QCOMPARE(info->transitionEvents(transitions.at(0)).size(), 0);
+
+ // <anon>->next
+ QCOMPARE(info->transitionType(transitions.at(1)), QScxmlStateMachineInfo::ExternalTransition);
+ QCOMPARE(info->transitionSource(transitions.at(1)), states.at(0));
+ QCOMPARE(info->transitionTargets(transitions.at(1)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(1)).at(0), states.at(1));
+ QCOMPARE(info->transitionEvents(transitions.at(1)).size(), 1);
+ QCOMPARE(info->transitionEvents(transitions.at(1)).at(0), QStringLiteral("step"));
+
+ // a->theEnd
+ QCOMPARE(info->transitionType(transitions.at(2)), QScxmlStateMachineInfo::ExternalTransition);
+ QCOMPARE(info->transitionSource(transitions.at(2)), states.at(2));
+ QCOMPARE(info->transitionTargets(transitions.at(2)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(2)).at(0), states.at(4));
+ QCOMPARE(info->transitionEvents(transitions.at(2)).size(), 1);
+ QCOMPARE(info->transitionEvents(transitions.at(2)).at(0), QStringLiteral("step"));
+
+ // b->theEnd
+ QCOMPARE(info->transitionType(transitions.at(3)), QScxmlStateMachineInfo::InternalTransition);
+ QCOMPARE(info->transitionSource(transitions.at(3)), states.at(3));
+ QCOMPARE(info->transitionTargets(transitions.at(3)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(3)).at(0), states.at(4));
+ QCOMPARE(info->transitionEvents(transitions.at(3)).size(), 1);
+ QCOMPARE(info->transitionEvents(transitions.at(3)).at(0), QStringLiteral("step"));
+
+ // initial transition that activates the first (anonymous) state
+ QCOMPARE(info->transitionType(transitions.at(4)), QScxmlStateMachineInfo::SyntheticTransition);
+ QCOMPARE(info->stateType(info->transitionSource(transitions.at(4))),
+ QScxmlStateMachineInfo::InvalidState);
+ QCOMPARE(info->transitionTargets(transitions.at(4)).size(), 1);
+ QCOMPARE(info->transitionTargets(transitions.at(4)).at(0), states.at(0));
+ QCOMPARE(info->transitionEvents(transitions.at(4)).size(), 0);
+
+ // "initial" transition in the next state that activates all sub-states
+ QCOMPARE(info->transitionType(transitions.at(5)), QScxmlStateMachineInfo::SyntheticTransition);
+ QCOMPARE(info->transitionSource(transitions.at(5)), states.at(1));
+ QCOMPARE(info->transitionTargets(transitions.at(5)).size(), 2);
+ QCOMPARE(info->transitionTargets(transitions.at(5)).at(0), states.at(2));
+ QCOMPARE(info->transitionTargets(transitions.at(5)).at(1), states.at(3));
+ QCOMPARE(info->transitionEvents(transitions.at(5)).size(), 0);
+
+ Recorder recorder;
+ QObject::connect(info, &QScxmlStateMachineInfo::statesEntered,
+ &recorder, &Recorder::statesEntered);
+ QObject::connect(info, &QScxmlStateMachineInfo::statesExited,
+ &recorder, &Recorder::statesExited);
+ QObject::connect(info, &QScxmlStateMachineInfo::transitionsTriggered,
+ &recorder, &Recorder::transitionsTriggered);
+ QObject::connect(stateMachine.data(), &QScxmlStateMachine::reachedStableState,
+ &recorder, &Recorder::reachedStableState);
+
+ // initial step into first anonymous state
+ stateMachine->start();
+ QVERIFY(recorder.finishMacroStep());
+ QCOMPARE(recorder.enterCount, 1);
+ QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 0);
+ QVERIFY(recorder.exited.isEmpty());
+
+ recorder.clear();
+
+ // step from anonymous state into the parallel state, which activates "a" and "b" (in THAT
+ // order!)
+ stateMachine->submitEvent("step");
+ QVERIFY(recorder.finishMacroStep());
+ QCOMPARE(recorder.enterCount, 1);
+ QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 1 << 2 << 3);
+ QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 0);
+ QCOMPARE(recorder.transitionTriggerCount, 1);
+ QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 1);
+
+ recorder.clear();
+
+ // step from the state "b" into "theEnd", which exits "b", "a", and "next" in exactly that
+ // order
+ stateMachine->submitEvent("step");
+ QVERIFY(recorder.finishMacroStep());
+ QCOMPARE(recorder.enterCount, 1);
+ QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 4);
+ QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 3 << 2 << 1);
+ QCOMPARE(recorder.transitionTriggerCount, 1);
+ QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 2);
+}
+
+
+QTEST_MAIN(tst_StateMachineInfo)
+
+#include "tst_statemachineinfo.moc"
+
+
diff --git a/tests/auto/statemachineinfo/tst_statemachineinfo.qrc b/tests/auto/statemachineinfo/tst_statemachineinfo.qrc
new file mode 100644
index 0000000..0e464d4
--- /dev/null
+++ b/tests/auto/statemachineinfo/tst_statemachineinfo.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/tst_statemachineinfo">
+ <file>statemachine.scxml</file>
+ </qresource>
+</RCC>
diff --git a/tools/qscxmlc/cppdatamodel.t b/tools/qscxmlc/cppdatamodel.t
new file mode 100644
index 0000000..2ca5b16
--- /dev/null
+++ b/tools/qscxmlc/cppdatamodel.t
@@ -0,0 +1,34 @@
+QString ${datamodel}::evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+${evaluateToStringCases}
+ Q_UNREACHABLE();
+ *ok = false;
+ return QString();
+}
+
+bool ${datamodel}::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+${evaluateToBoolCases}
+ Q_UNREACHABLE();
+ *ok = false;
+ return false;
+}
+
+QVariant ${datamodel}::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+${evaluateToVariantCases}
+ Q_UNREACHABLE();
+ *ok = false;
+ return QVariant();
+}
+
+void ${datamodel}::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+${evaluateToVoidCases}
+ Q_UNREACHABLE();
+ *ok = false;
+}
diff --git a/tools/qscxmlc/data.t b/tools/qscxmlc/data.t
new file mode 100644
index 0000000..a813d01
--- /dev/null
+++ b/tools/qscxmlc/data.t
@@ -0,0 +1,135 @@
+struct ${classname}::Data: private QScxmlTableData {
+ Data(${classname} &stateMachine)
+ : stateMachine(stateMachine)
+ {}
+
+ void init() {
+ stateMachine.setTableData(this);
+ ${dataModelInitialization}
+ }
+
+ QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return ${name}; }
+
+ QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return ${initialSetup}; }
+
+ QScxmlExecutableContent::InstructionId *instructions() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return theInstructions; }
+
+ QScxmlExecutableContent::StringId *dataNames(int *count) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { *count = ${dataNameCount}; return dataIds; }
+
+ QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { Q_ASSERT(evaluatorId >= 0); Q_ASSERT(evaluatorId < ${evaluatorCount}); return evaluators[evaluatorId]; }
+
+ QScxmlExecutableContent::AssignmentInfo assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { Q_ASSERT(assignmentId >= 0); Q_ASSERT(assignmentId < ${assignmentCount}); return assignments[assignmentId]; }
+
+ QScxmlExecutableContent::ForeachInfo foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { Q_ASSERT(foreachId >= 0); Q_ASSERT(foreachId < ${foreachCount}); return foreaches[foreachId]; }
+
+ QString string(QScxmlExecutableContent::StringId id) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ Q_ASSERT(id >= QScxmlExecutableContent::NoString); Q_ASSERT(id < ${stringCount});
+ if (id == QScxmlExecutableContent::NoString) return QString();
+ return QString({static_cast<QStringData*>(strings.data + id)});
+ }
+
+ const qint32 *stateMachineTable() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return theStateMachineTable; }
+
+ QScxmlInvokableServiceFactory *serviceFactory(int id) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+
+ ${classname} &stateMachine;
+ ${dataModelField}
+
+ static QScxmlExecutableContent::ParameterInfo param(QScxmlExecutableContent::StringId name,
+ QScxmlExecutableContent::EvaluatorId expr,
+ QScxmlExecutableContent::StringId location)
+ {
+ QScxmlExecutableContent::ParameterInfo p;
+ p.name = name;
+ p.expr = expr;
+ p.location = location;
+ return p;
+ }
+
+ static QScxmlExecutableContent::InvokeInfo invoke(
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId prefix,
+ QScxmlExecutableContent::EvaluatorId expr,
+ QScxmlExecutableContent::StringId location,
+ QScxmlExecutableContent::StringId context,
+ QScxmlExecutableContent::ContainerId finalize,
+ bool autoforward)
+ {
+ QScxmlExecutableContent::InvokeInfo i;
+ i.id = id;
+ i.prefix = prefix;
+ i.expr = expr;
+ i.location = location;
+ i.context = context;
+ i.finalize = finalize;
+ i.autoforward = autoforward;
+ return i;
+ }
+
+ static qint32 theInstructions[];
+ static QScxmlExecutableContent::StringId dataIds[];
+ static QScxmlExecutableContent::EvaluatorInfo evaluators[];
+ static QScxmlExecutableContent::AssignmentInfo assignments[];
+ static QScxmlExecutableContent::ForeachInfo foreaches[];
+ static const qint32 theStateMachineTable[];
+ static struct Strings {
+ QArrayData data[${stringCount}];
+ qunicodechar stringdata[${stringdataSize}];
+ } strings;
+};
+
+${classname}::${classname}(QObject *parent)
+ : QScxmlStateMachine(&staticMetaObject, parent)
+ , data(new Data(*this))
+{ qRegisterMetaType<${classname} *>(); data->init(); }
+
+${classname}::~${classname}()
+{ delete data; }
+
+QScxmlInvokableServiceFactory *${classname}::Data::serviceFactory(int id) const
+{
+${serviceFactories}
+}
+
+qint32 ${classname}::Data::theInstructions[] = {
+${theInstructions}
+};
+
+QScxmlExecutableContent::StringId ${classname}::Data::dataIds[] = {
+${dataIds}
+};
+
+QScxmlExecutableContent::EvaluatorInfo ${classname}::Data::evaluators[] = {
+${evaluators}
+};
+
+QScxmlExecutableContent::AssignmentInfo ${classname}::Data::assignments[] = {
+${assignments}
+};
+
+QScxmlExecutableContent::ForeachInfo ${classname}::Data::foreaches[] = {
+${foreaches}
+};
+
+#define STR_LIT(idx, ofs, len) \
+ Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
+ qptrdiff(offsetof(Strings, stringdata) + ofs * sizeof(qunicodechar) - idx * sizeof(QArrayData)) \
+ )
+${classname}::Data::Strings ${classname}::Data::strings = {{
+${strLits}
+},{
+${uniLits}
+}};
+
+const qint32 ${classname}::Data::theStateMachineTable[] = ${theStateMachineTable};
+
+${metaObject}
diff --git a/tools/qscxmlc/decl.t b/tools/qscxmlc/decl.t
new file mode 100644
index 0000000..9482885
--- /dev/null
+++ b/tools/qscxmlc/decl.t
@@ -0,0 +1,16 @@
+class ${classname}: public QScxmlStateMachine
+{
+ /* qmake ignore Q_OBJECT */
+ Q_OBJECT
+${properties}
+
+public:
+ ${classname}(QObject *parent = 0);
+ ~${classname}();
+
+private:
+ struct Data;
+ friend struct Data;
+ struct Data *data;
+};
+
diff --git a/tools/qscxmlc/doc/qscxmlc.qdoc b/tools/qscxmlc/doc/qscxmlc.qdoc
index 619390a..07b5c8d 100644
--- a/tools/qscxmlc/doc/qscxmlc.qdoc
+++ b/tools/qscxmlc/doc/qscxmlc.qdoc
@@ -65,11 +65,9 @@
\li Option
\li Description
\row
- \li \c --no-c++11
- \li Use no C++11 features in the generated code.
- \row
\li \c {--namespace <namespace>}
- \li Put the generated class(es) in the specified namespace.
+ \li Put the generated class(es) in the specified namespace. You can use the
+ \c QSCXMLC_NAMESPACE variable to specify this in your project file.
\row
\li \c {-o <base/out/name>}
\li The base name of the output files. This can include a path. If none is specified, the
diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp
index b37e928..9082efa 100644
--- a/tools/qscxmlc/generator.cpp
+++ b/tools/qscxmlc/generator.cpp
@@ -37,6 +37,7 @@
#include <QtCore/qplugin.h>
#include <private/qmetaobject_p.h> //for the flags.
+#include <stdio.h>
QT_BEGIN_NAMESPACE
@@ -1146,13 +1147,13 @@ void Generator::generateStaticMetacall()
Q_ASSERT(!f.normalizedType.isEmpty());
fprintf(out, " case %d: ", methodindex);
- //----
+ //---- Changed from the original in moc
if (f.implementation) {
fprintf(out, f.implementation, methodindex);
fprintf(out, " break;\n");
continue;
}
- //----
+ //---- End of change
if (f.normalizedType != "void")
fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData());
@@ -1327,8 +1328,13 @@ void Generator::generateStaticMetacall()
fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n",
propindex, prefix.constData(), p.read.constData());
else if (!p.read.isEmpty())
- fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n",
- propindex, p.type.constData(), prefix.constData(), p.read.constData());
+ //---- Changed from the original in moc
+ {
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.read.constData(),
+ p.read.endsWith(')') ? "" : "()");
+ }
+ //---- End of change
else
fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n",
propindex, p.type.constData(), prefix.constData(), p.member.constData());
diff --git a/tools/qscxmlc/generator.h b/tools/qscxmlc/generator.h
index b22b9e3..21d82d3 100644
--- a/tools/qscxmlc/generator.h
+++ b/tools/qscxmlc/generator.h
@@ -30,9 +30,9 @@
#define GENERATOR_H
#include "moc.h"
-#include <QHash>
-#include <QVector>
-#include <QIODevice>
+#include <QtCore/qhash.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qiodevice.h>
QT_BEGIN_NAMESPACE
diff --git a/tools/qscxmlc/main.cpp b/tools/qscxmlc/main.cpp
index b401af8..ccc99ed 100644
--- a/tools/qscxmlc/main.cpp
+++ b/tools/qscxmlc/main.cpp
@@ -30,6 +30,7 @@
#include "qscxmlc.h"
#include <QCoreApplication>
+#include <QStringList>
int main(int argc, char *argv[])
{
diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h
index 5149454..36cff5f 100644
--- a/tools/qscxmlc/moc.h
+++ b/tools/qscxmlc/moc.h
@@ -29,13 +29,10 @@
#ifndef MOC_H
#define MOC_H
-#include <qstringlist.h>
-#include <qmap.h>
-#include <qpair.h>
-#include <qjsondocument.h>
-#include <qjsonarray.h>
-#include <qjsonobject.h>
-#include <stdio.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonarray.h>
#include <ctype.h>
QT_BEGIN_NAMESPACE
diff --git a/tools/qscxmlc/qscxmlc.cpp b/tools/qscxmlc/qscxmlc.cpp
index d5217d8..89abdaa 100644
--- a/tools/qscxmlc/qscxmlc.cpp
+++ b/tools/qscxmlc/qscxmlc.cpp
@@ -26,7 +26,7 @@
**
****************************************************************************/
-#include <QtScxml/private/qscxmlparser_p.h>
+#include <QtScxml/private/qscxmlcompiler_p.h>
#include <QtScxml/qscxmltabledata.h>
#include "scxmlcppdumper.h"
#include "qscxmlc.h"
@@ -89,12 +89,12 @@ int write(TranslationUnit *tu)
return NoError;
}
-static void collectAllDocuments(DocumentModel::ScxmlDocument *doc, QMap<DocumentModel::ScxmlDocument *, QString> *docs)
+static void collectAllDocuments(DocumentModel::ScxmlDocument *doc,
+ QList<DocumentModel::ScxmlDocument *> *docs)
{
- docs->insert(doc, doc->root->name);
- foreach (DocumentModel::ScxmlDocument *subDoc, doc->allSubDocuments) {
+ docs->append(doc);
+ for (DocumentModel::ScxmlDocument *subDoc : qAsConst(doc->allSubDocuments))
collectAllDocuments(subDoc, docs);
- }
}
int run(const QStringList &arguments)
@@ -107,8 +107,6 @@ int run(const QStringList &arguments)
cmdParser.setApplicationDescription(QCoreApplication::translate("main",
"Compiles the given input.scxml file to a header and a cpp file."));
- QCommandLineOption optionNoCxx11(QLatin1String("no-c++11"),
- QCoreApplication::translate("main", "Don't use C++11 in generated code."));
QCommandLineOption optionNamespace(QLatin1String("namespace"),
QCoreApplication::translate("main", "Put generated code into <namespace>."),
QCoreApplication::translate("main", "namespace"));
@@ -124,23 +122,14 @@ int run(const QStringList &arguments)
QCommandLineOption optionClassName(QLatin1String("classname"),
QCoreApplication::translate("main", "Generate <name> for state machine class name."),
QCoreApplication::translate("main", "name"));
- QCommandLineOption optionQtMode(QLatin1String("qt-mode"),
- QCoreApplication::translate("main", "Enables or disables Qt mode. "
- "In order to unconditionally enable qt-mode, specify \"yes\". "
- "To unconditionally disable qt-mode, specify \"no\". "
- "To read the setting from the input file, specify \"from-input\". "
- "The default is \"from-input\"."),
- QCoreApplication::translate("main", "mode"), QLatin1String("from-input"));
cmdParser.addPositionalArgument(QLatin1String("input"),
QCoreApplication::translate("main", "Input SCXML file."));
- cmdParser.addOption(optionNoCxx11);
cmdParser.addOption(optionNamespace);
cmdParser.addOption(optionOutputBaseName);
cmdParser.addOption(optionOutputHeaderName);
cmdParser.addOption(optionOutputSourceName);
cmdParser.addOption(optionClassName);
- cmdParser.addOption(optionQtMode);
cmdParser.process(arguments);
@@ -160,28 +149,12 @@ int run(const QStringList &arguments)
const QString scxmlFileName = inputFiles.at(0);
TranslationUnit options;
- options.useCxx11 = !cmdParser.isSet(optionNoCxx11);
if (cmdParser.isSet(optionNamespace))
options.namespaceName = cmdParser.value(optionNamespace);
QString outFileName = cmdParser.value(optionOutputBaseName);
QString outHFileName = cmdParser.value(optionOutputHeaderName);
QString outCppFileName = cmdParser.value(optionOutputSourceName);
QString mainClassName = cmdParser.value(optionClassName);
- QString qtModeName = cmdParser.value(optionQtMode);
-
- QScxmlParser::QtMode qtMode = QScxmlParser::QtModeFromInputFile;
-
- if (qtModeName == QLatin1String("yes")) {
- qtMode = QScxmlParser::QtModeEnabled;
- } else if (qtModeName == QLatin1String("no")) {
- qtMode = QScxmlParser::QtModeDisabled;
- } else if (qtModeName == QLatin1String("from-input")) {
- qtMode = QScxmlParser::QtModeFromInputFile;
- } else {
- errs << QCoreApplication::translate("main", "Error: unexpected value for qt-mode option: %1")
- .arg(qtModeName) << endl;
- cmdParser.showHelp(CommandLineArgumentsError);
- }
if (outFileName.isEmpty())
outFileName = QFileInfo(scxmlFileName).baseName();
@@ -197,28 +170,27 @@ int run(const QStringList &arguments)
}
QXmlStreamReader reader(&file);
- QScxmlParser parser(&reader);
- parser.setFileName(file.fileName());
- parser.setQtMode(qtMode);
- parser.parse();
- if (!parser.errors().isEmpty()) {
- foreach (const QScxmlError &error, parser.errors()) {
+ QScxmlCompiler compiler(&reader);
+ compiler.setFileName(file.fileName());
+ compiler.compile();
+ if (!compiler.errors().isEmpty()) {
+ const auto errors = compiler.errors();
+ for (const QScxmlError &error : errors) {
errs << error.toString() << endl;
}
return ParseError;
}
- auto mainDoc = QScxmlParserPrivate::get(&parser)->scxmlDocument();
+ auto mainDoc = QScxmlCompilerPrivate::get(&compiler)->scxmlDocument();
if (mainDoc == nullptr) {
- Q_ASSERT(!parser.errors().isEmpty());
- foreach (const QScxmlError &error, parser.errors()) {
+ Q_ASSERT(!compiler.errors().isEmpty());
+ const auto errors = compiler.errors();
+ for (const QScxmlError &error : errors) {
errs << error.toString() << endl;
}
return ScxmlVerificationError;
}
- QMap<DocumentModel::ScxmlDocument *, QString> docs;
- collectAllDocuments(mainDoc, &docs);
if (mainClassName.isEmpty())
mainClassName = mainDoc->root->name;
if (mainClassName.isEmpty()) {
@@ -227,19 +199,33 @@ int run(const QStringList &arguments)
if (dot != -1)
mainClassName = mainClassName.left(dot);
}
- docs.insert(mainDoc, mainClassName);
+
+ QList<DocumentModel::ScxmlDocument *> docs;
+ collectAllDocuments(mainDoc, &docs);
TranslationUnit tu = options;
+ tu.allDocuments = docs;
tu.scxmlFileName = QFileInfo(file).fileName();
tu.mainDocument = mainDoc;
tu.outHFileName = outHFileName;
tu.outCppFileName = outCppFileName;
- for (QMap<DocumentModel::ScxmlDocument *, QString>::const_iterator i = docs.begin(), ei = docs.end(); i != ei; ++i) {
- auto name = i.value();
+ tu.classnameForDocument.insert(mainDoc, mainClassName);
+
+ docs.pop_front();
+
+ for (DocumentModel::ScxmlDocument *doc : qAsConst(docs)) {
+ auto name = doc->root->name;
+ auto prefix = name;
if (name.isEmpty()) {
- name = QStringLiteral("%1_StateMachine_%2").arg(mainClassName).arg(tu.classnameForDocument.size() + 1);
+ prefix = QStringLiteral("%1_StateMachine").arg(mainClassName);
+ name = prefix;
}
- tu.classnameForDocument.insert(i.key(), name);
+
+ int counter = 1;
+ while (tu.classnameForDocument.key(name) != nullptr)
+ name = QStringLiteral("%1_%2").arg(prefix).arg(++counter);
+
+ tu.classnameForDocument.insert(doc, name);
}
return write(&tu);
diff --git a/tools/qscxmlc/qscxmlc.pri b/tools/qscxmlc/qscxmlc.pri
index 17afe4b..4c7c991 100644
--- a/tools/qscxmlc/qscxmlc.pri
+++ b/tools/qscxmlc/qscxmlc.pri
@@ -14,8 +14,8 @@ HEADERS += \
$$PWD/scxmlcppdumper.h
HEADERS += \
- $$PWD/../../src/scxml/qscxmlparser.h \
- $$PWD/../../src/scxml/qscxmlparser_p.h \
+ $$PWD/../../src/scxml/qscxmlcompiler.h \
+ $$PWD/../../src/scxml/qscxmlcompiler_p.h \
$$PWD/../../src/scxml/qscxmlglobals.h \
$$PWD/../../src/scxml/qscxmlexecutablecontent.h \
$$PWD/../../src/scxml/qscxmlexecutablecontent_p.h \
@@ -23,7 +23,7 @@ HEADERS += \
$$PWD/../../src/scxml/qscxmltabledata.h
SOURCES += \
- $$PWD/../../src/scxml/qscxmlparser.cpp \
+ $$PWD/../../src/scxml/qscxmlcompiler.cpp \
$$PWD/../../src/scxml/qscxmlexecutablecontent.cpp \
$$PWD/../../src/scxml/qscxmlerror.cpp \
$$PWD/../../src/scxml/qscxmltabledata.cpp
diff --git a/tools/qscxmlc/qscxmlc.pro b/tools/qscxmlc/qscxmlc.pro
index 7621eef..7620d3d 100644
--- a/tools/qscxmlc/qscxmlc.pro
+++ b/tools/qscxmlc/qscxmlc.pro
@@ -11,3 +11,6 @@ SOURCES += \
main.cpp
load(qt_tool)
+load(resources)
+
+RESOURCES += templates.qrc
diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp
index 46ee774..b539e0d 100644
--- a/tools/qscxmlc/scxmlcppdumper.cpp
+++ b/tools/qscxmlc/scxmlcppdumper.cpp
@@ -26,18 +26,25 @@
**
****************************************************************************/
-#include <QtScxml/private/qscxmlexecutablecontent_p.h>
#include "scxmlcppdumper.h"
+#include "generator.h"
+
+#include <QtScxml/private/qscxmlexecutablecontent_p.h>
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qbuffer.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qresource.h>
#include <algorithm>
#include <functional>
-#include <QFileInfo>
-#include <QBuffer>
-
-#include "generator.h"
QT_BEGIN_NAMESPACE
+using namespace QScxmlInternal;
+
+namespace {
+
static const QString doNotEditComment = QString::fromLatin1(
"//\n"
"// Statemachine code from reading SCXML file '%1'\n"
@@ -56,91 +63,6 @@ static const QString revisionCheck = QString::fromLatin1(
"#endif\n"
);
-struct StringListDumper {
- StringListDumper &operator <<(const QString &s) {
- text.append(s);
- return *this;
- }
-
- StringListDumper &operator <<(const QLatin1String &s) {
- text.append(s);
- return *this;
- }
- StringListDumper &operator <<(const char *s) {
- text.append(QLatin1String(s));
- return *this;
- }
- StringListDumper &operator <<(int i) {
- text.append(QString::number(i));
- return *this;
- }
- StringListDumper &operator <<(const QByteArray &s) {
- text.append(QString::fromUtf8(s));
- return *this;
- }
-
- bool isEmpty() const {
- return text.isEmpty();
- }
-
- void write(QTextStream &out, const QString &prefix, const QString &suffix, const QString &mainClassName = QString()) const
- {
- foreach (QString line, text) {
- if (!mainClassName.isEmpty() && line.contains(QStringLiteral("%"))) {
- line = line.arg(mainClassName);
- }
- out << prefix << line << suffix;
- }
- }
-
- void unique()
- {
- text.sort();
- text.removeDuplicates();
- }
-
- QStringList text;
-};
-
-struct Method {
- StringListDumper initializer;
- Method(const QString &decl = QString()): decl(decl) {}
- Method(const StringListDumper &impl): impl(impl) {}
- QString decl; // void f(int i = 0);
- StringListDumper impl; // void f(int i) { m_i = ++i; }
-};
-
-struct ClassDump {
- bool needsEventFilter;
- StringListDumper implIncludes;
- QString className;
- QString dataModelClassName;
- StringListDumper classFields;
- StringListDumper tables;
- Method init;
- Method initDataModel;
- StringListDumper dataMethods;
- StringListDumper classMethods;
- Method constructor;
- Method destructor;
- StringListDumper properties;
- StringListDumper signalMethods;
- QList<Method> publicMethods;
- QList<Method> protectedMethods;
- StringListDumper publicSlotDeclarations;
- StringListDumper publicSlotDefinitions;
-
- QList<Method> dataModelMethods;
-
- ClassDump()
- : needsEventFilter(false)
- {}
-
- QByteArray metaData;
-};
-
-namespace {
-
QString cEscape(const QString &str)
{
QString res;
@@ -179,1133 +101,316 @@ QString cEscape(const QString &str)
return str;
}
+typedef QHash<QString, QString> Replacements;
+static void genTemplate(QTextStream &out, const QString &filename, const Replacements &replacements)
+{
+ QResource file(filename);
+ if (!file.isValid()) {
+ qFatal("Unable to open template '%s'", qPrintable(filename));
+ }
+ QByteArray data;
+ if (file.isCompressed() && file.size()) {
+ data = qUncompress(file.data(), int(file.size()));
+ } else {
+ data = QByteArray::fromRawData(reinterpret_cast<const char *>(file.data()),
+ int(file.size()));
+ }
+ const QString t = QString::fromLatin1(data);
+ data.clear();
+
+ int start = 0;
+ for (int openIdx = t.indexOf(QStringLiteral("${"), start); openIdx >= 0; openIdx =
+ t.indexOf(QStringLiteral("${"), start)) {
+ out << t.midRef(start, openIdx - start);
+ openIdx += 2;
+ const int closeIdx = t.indexOf(QLatin1Char('}'), openIdx);
+ Q_ASSERT(closeIdx >= openIdx);
+ QString key = t.mid(openIdx, closeIdx - openIdx);
+ if (!replacements.contains(key)) {
+ qFatal("Replacing '%s' failed: no replacement found", qPrintable(key));
+ }
+ out << replacements.value(key);
+ start = closeIdx + 1;
+ }
+ out << t.midRef(start);
+}
+
static const char *headerStart =
"#include <QScxmlStateMachine>\n"
"#include <QString>\n"
- "#include <QByteArray>\n"
+ "#include <QVariant>\n"
"\n";
using namespace DocumentModel;
-enum class Evaluator
+QString createContainer(const QStringList &elements)
{
- ToVariant,
- ToString,
- ToBool,
- Assignment,
- Foreach,
- Script
-};
-
-class DumperVisitor: public QScxmlExecutableContent::Builder
-{
- Q_DISABLE_COPY(DumperVisitor)
-
-public:
- DumperVisitor(ClassDump &clazz, TranslationUnit *tu)
- : namespacePrefix(QStringLiteral("::"))
- , clazz(clazz)
- , translationUnit(tu)
- , m_bindLate(false)
- , m_qtMode(false)
- {
- if (!tu->namespaceName.isEmpty()) {
- namespacePrefix += QStringLiteral("%1::").arg(tu->namespaceName);
- }
- }
-
- void process(ScxmlDocument *doc)
- {
- Q_ASSERT(doc);
-
- clazz.className = mangleIdentifier(translationUnit->classnameForDocument.value(doc));
- m_qtMode = doc->qtMode;
-
- doc->root->accept(this);
-
- addSubStateMachineProperties(doc);
- addEvents();
-
- generateMetaObject();
- generateTables();
- }
-
- ~DumperVisitor()
- {
- Q_ASSERT(m_parents.isEmpty());
- }
-
-protected:
- using NodeVisitor::visit;
-
- bool visit(Scxml *node) Q_DECL_OVERRIDE
- {
- // init:
- if (!node->name.isEmpty()) {
- clazz.dataMethods << QStringLiteral("QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return string(%1); }").arg(addString(node->name))
- << QString();
- clazz.init.impl << QStringLiteral("stateMachine.setObjectName(string(%1));").arg(addString(node->name));
- } else {
- clazz.dataMethods << QStringLiteral("QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return QString(); }")
- << QString();
- }
- if (node->dataModel == Scxml::CppDataModel) {
- // Tell the builder not to generate any script strings when visiting any executable content.
- // We'll take care of the evaluators ourselves.
- setIsCppDataModel(true);
- }
-
- QString binding;
- switch (node->binding) {
- case Scxml::EarlyBinding:
- binding = QStringLiteral("Early");
- break;
- case Scxml::LateBinding:
- binding = QStringLiteral("Late");
- m_bindLate = true;
- break;
- default:
- Q_UNREACHABLE();
- }
- clazz.init.impl << QStringLiteral("stateMachine.setDataBinding(QScxmlStateMachine::%1Binding);").arg(binding);
- clazz.implIncludes << QStringLiteral("qscxmlexecutablecontent.h");
- clazz.init.impl << QStringLiteral("stateMachine.setTableData(this);");
-
- foreach (AbstractState *s, node->initialStates) {
- clazz.init.impl << QStringLiteral("%1.setAsInitialStateFor(&stateMachine);")
- .arg(mangledName(s, StateName));
- }
-
- // visit the kids:
- m_parents.append(node);
- visit(node->children);
- visit(node->dataElements);
-
- m_dataElements.append(node->dataElements);
- if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) {
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return %1; }").arg(startNewSequence())
- << QString();
- generate(m_dataElements);
- if (node->script) {
- node->script->accept(this);
- }
- visit(&node->initialSetup);
- endSequence();
- } else {
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return QScxmlExecutableContent::NoInstruction; }")
- << QString();
- }
-
- m_parents.removeLast();
-
- { // the data model:
- switch (node->dataModel) {
- case Scxml::NullDataModel:
- clazz.classFields << QStringLiteral("QScxmlNullDataModel dataModel;");
- clazz.implIncludes << QStringLiteral("QScxmlNullDataModel");
- clazz.init.impl << QStringLiteral("stateMachine.setDataModel(&dataModel);");
- break;
- case Scxml::JSDataModel:
- clazz.classFields << QStringLiteral("QScxmlEcmaScriptDataModel dataModel;");
- clazz.implIncludes << QStringLiteral("QScxmlEcmaScriptDataModel");
- clazz.init.impl << QStringLiteral("stateMachine.setDataModel(&dataModel);");
- break;
- case Scxml::CppDataModel:
- clazz.dataModelClassName = node->cppDataModelClassName;
- clazz.implIncludes << node->cppDataModelHeaderName;
- break;
- default:
- Q_UNREACHABLE();
- }
- }
- return false;
- }
-
- bool visit(State *node) Q_DECL_OVERRIDE
- {
- QString name = mangledName(node, PlainName);
- QString stateName = mangledName(node, StateName);
- // Property stuff:
- if (isValidQPropertyName(node->id)) {
- clazz.properties << QStringLiteral("Q_PROPERTY(bool %1 READ %2 NOTIFY %3)")
- .arg(node->id).arg(name)
- .arg(mangledName(node, SignalName));
- }
- if (m_qtMode) {
- Method getter(QStringLiteral("bool %1() const").arg(name));
- getter.impl << QStringLiteral("bool %2::%1() const").arg(name)
- << QStringLiteral("{ return data->%1.active(); }").arg(stateName);
- clazz.publicMethods << getter;
- }
-
- // Declaration:
- if (node->type == State::Final) {
- clazz.classFields << QStringLiteral("QScxmlFinalState ") + stateName + QLatin1Char(';');
- } else {
- clazz.classFields << QStringLiteral("QScxmlState ") + stateName + QLatin1Char(';');
- }
-
- // Initializer:
- clazz.constructor.initializer << generateInitializer(node);
-
- // init:
- if (!node->id.isEmpty()) {
- clazz.init.impl << stateName + QStringLiteral(".setObjectName(string(%1));").arg(addString(node->id));
- }
- if (node->type == State::Parallel) {
- clazz.init.impl << stateName + QStringLiteral(".setChildMode(QState::ParallelStates);");
- } else {
- foreach (AbstractState *initialState, node->initialStates) {
- clazz.init.impl << stateName + QStringLiteral(".setInitialState(&")
- + mangledName(initialState, StateName)
- + QStringLiteral(");");
- }
-
- }
- if (!node->id.isEmpty()) {
- clazz.init.impl << QStringLiteral("QObject::connect(&")
- + stateName
- + QStringLiteral(", SIGNAL(activeChanged(bool)), &stateMachine, SIGNAL(")
- + mangledName(node, SignalName)
- + QStringLiteral("(bool)));");
- }
-
- m_stateNames.append(node->id);
- m_stateFieldNames.append(stateName);
-
- // visit the kids:
- m_parents.append(node);
- if (!node->dataElements.isEmpty()) {
- if (m_bindLate) {
- clazz.init.impl << stateName + QStringLiteral(".setInitInstructions(%1);").arg(startNewSequence());
- generate(node->dataElements);
- endSequence();
- } else {
- m_dataElements.append(node->dataElements);
- }
- }
-
- visit(node->children);
- if (!node->onEntry.isEmpty())
- clazz.init.impl << stateName + QStringLiteral(".setOnEntryInstructions(%1);").arg(generate(node->onEntry));
- if (!node->onExit.isEmpty())
- clazz.init.impl << stateName + QStringLiteral(".setOnExitInstructions(%1);").arg(generate(node->onExit));
- if (!node->invokes.isEmpty()) {
- QStringList lines;
- for (int i = 0, ei = node->invokes.size(); i != ei; ++i) {
- 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(addString(invoke->id));
- line += QStringLiteral("%1, ").arg(addString(node->id + QStringLiteral(".session-")));
- line += QStringLiteral("%1, ").arg(addString(invoke->idLocation));
- {
- QStringList l;
- foreach (const QString &name, invoke->namelist) {
- l.append(QString::number(addString(name)));
- }
- line += QStringLiteral("%1, ").arg(createVector(QStringLiteral("QScxmlExecutableContent::StringId"), l));
- }
- line += QStringLiteral("%1, ").arg(invoke->autoforward ? QStringLiteral("true") : QStringLiteral("false"));
- {
- QStringList l;
- foreach (DocumentModel::Param *param, invoke->params) {
- l += QStringLiteral("QScxmlInvokableServiceFactory::Param(%1, %2, %3)")
- .arg(addString(param->name))
- .arg(createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"), param->expr))
- .arg(addString(param->location));
- }
- line += QStringLiteral("%1, ").arg(createVector(QStringLiteral("QScxmlInvokableServiceFactory::Param"), l));
- }
- if (invoke->finalize.isEmpty()) {
- line += QStringLiteral("QScxmlExecutableContent::NoInstruction");
- } else {
- line += QString::number(startNewSequence());
- visit(&invoke->finalize);
- endSequence();
- }
- line += QLatin1Char(')');
- lines << line;
- }
- clazz.init.impl << stateName + QStringLiteral(".setInvokableServiceFactories(");
- clazz.init.impl << QStringLiteral(" ") + createVector(QStringLiteral("QScxmlInvokableServiceFactory *"), lines);
- clazz.init.impl << QStringLiteral(");");
- }
-
- if (node->type == State::Final) {
- auto id = generate(node->doneData);
- clazz.init.impl << stateName + QStringLiteral(".setDoneData(%1);").arg(id);
- }
-
- m_parents.removeLast();
- return false;
- }
-
- bool visit(Transition *node) Q_DECL_OVERRIDE
- {
- const QString tName = transitionName(node);
- if (m_qtMode) {
- foreach (const QString &event, node->events) {
- if (!DocumentModel::isEventToBeGenerated(event))
- continue;
-
- // If the event name is not filtered out, is was already validated inside:
- // bool ScxmlVerifier::visit(DocumentModel::Transition *transition)
- // by a call to: validateEventName();
- m_knownEvents.insert(event);
- }
- }
-
- // Declaration:
- clazz.classFields << QStringLiteral("QScxmlTransition ") + tName + QLatin1Char(';');
-
- // Initializer:
- QString initializer = tName + QStringLiteral("(");
- QStringList elements;
- foreach (const QString &event, node->events)
- elements.append(qba(event));
- initializer += createList(QStringLiteral("QString"), elements);
- initializer += QStringLiteral(")");
- clazz.constructor.initializer << initializer;
-
- // init:
- if (node->condition) {
- QString condExpr = *node->condition.data();
- auto cond = createEvaluatorBool(QStringLiteral("transition"), QStringLiteral("cond"), condExpr);
- clazz.init.impl << tName + QStringLiteral(".setConditionalExpression(%1);").arg(cond);
- }
-
- if (m_parents.last()->asHistoryState()) {
- clazz.init.impl << QStringLiteral("%1.setDefaultTransition(&%2);").arg(parentStateMemberName(), tName);
- } else {
- clazz.init.impl << QStringLiteral("%1.addTransitionTo(&%2);").arg(tName, parentStateMemberName());
- }
-
- if (node->type == Transition::Internal) {
- clazz.init.impl << tName + QStringLiteral(".setTransitionType(QAbstractTransition::InternalTransition);");
- }
- QStringList targetNames;
- foreach (DocumentModel::AbstractState *s, node->targetStates)
- targetNames.append(QStringLiteral("&") + mangledName(s, StateName));
- QString targets = tName + QStringLiteral(".setTargetStates(") + createList(QStringLiteral("QAbstractState*"), targetNames);
- clazz.init.impl << targets + QStringLiteral(");");
-
- // visit the kids:
- if (!node->instructionsOnTransition.isEmpty()) {
- m_parents.append(node);
- m_currentTransitionName = tName;
- clazz.init.impl << tName + QStringLiteral(".setInstructionsOnTransition(%1);").arg(startNewSequence());
- visit(&node->instructionsOnTransition);
- endSequence();
- m_parents.removeLast();
- m_currentTransitionName.clear();
- }
- return false;
+ QString result;
+ if (elements.isEmpty()) {
+ result += QStringLiteral("{}");
+ } else {
+ result += QStringLiteral("{ ") + elements.join(QStringLiteral(", ")) + QStringLiteral(" }");
}
+ return result;
+}
- bool visit(DocumentModel::HistoryState *node) Q_DECL_OVERRIDE
- {
- // Includes:
- clazz.implIncludes << "QScxmlHistoryState";
-
- const QString stateName = mangledName(node, StateName);
- // Declaration:
- clazz.classFields << QStringLiteral("QScxmlHistoryState ") + stateName + QLatin1Char(';');
-
- // Initializer:
- clazz.constructor.initializer << generateInitializer(node);
-
- // init:
- if (!node->id.isEmpty()) {
- clazz.init.impl << stateName + QStringLiteral(".setObjectName(string(%1));").arg(addString(node->id));
- }
- QString depth;
- switch (node->type) {
- case DocumentModel::HistoryState::Shallow:
- depth = QStringLiteral("Shallow");
- break;
- case DocumentModel::HistoryState::Deep:
- depth = QStringLiteral("Deep");
+static void generateList(QString &out, std::function<QString(int)> next)
+{
+ const int maxLineLength = 80;
+ QString line;
+ for (int i = 0; ; ++i) {
+ const QString nr = next(i);
+ if (nr.isNull())
break;
- default:
- Q_UNREACHABLE();
- }
- clazz.init.impl << stateName + QStringLiteral(".setHistoryType(QScxmlHistoryState::") + depth + QStringLiteral("History);");
-
- // visit the kid:
- if (Transition *t = node->defaultConfiguration()) {
-
- m_parents.append(node);
- t->accept(this);
- m_parents.removeLast();
- }
- return false;
- }
-
- bool visit(Send *node) Q_DECL_OVERRIDE
- {
- if (m_qtMode && node->type == QStringLiteral("qt:signal")) {
- if (!m_signals.contains(node->event)) {
- m_signals.insert(node->event);
- m_signalNames.append(node->event);
- clazz.signalMethods << QStringLiteral("void %1(const QVariant &data);").arg(node->event);
- }
- }
-
- return QScxmlExecutableContent::Builder::visit(node);
- }
-
-private:
- enum NameForm {
- PlainName,
- SignalName,
- MachineName,
- StateName
- };
-
- QString mangledName(const QString &id, NameForm form) const
- {
- QString name = id;
- switch (form) {
- case PlainName: break;
- case SignalName: name.append(QStringLiteral("Changed")); break;
- case StateName: name.prepend(QStringLiteral("state_")); break;
- case MachineName: name.prepend(QStringLiteral("machine_")); break;
- }
-
- return name.isEmpty() ? name : mangleIdentifier(name);
- }
-
- QString mangledName(AbstractState *state, NameForm form) const
- {
- Q_ASSERT(state);
-
- QString name = m_mangledNames.value(state)[form];
- if (!name.isEmpty())
- return name;
-
- QString id = state->id;
- if (State *s = state->asState()) {
- if (s->type == State::Initial) {
- id = s->parent->asState()->id + QStringLiteral("_initial");
- }
- }
- name = mangledName(id, form);
- m_mangledNames[state][form] = name;
- return name;
- }
+ if (i != 0)
+ line += QLatin1Char(',');
- QString transitionName(Transition *t) const
- {
- int idx = 0;
- QString parentName;
- auto parent = m_parents.last();
- if (State *parentState = parent->asState()) {
- parentName = mangledName(parentState, PlainName);
- idx = childIndex(t, parentState->children);
- } else if (HistoryState *historyState = parent->asHistoryState()) {
- parentName = mangledName(historyState, PlainName);
- } else if (Scxml *scxml = parent->asScxml()) {
- parentName = QStringLiteral("stateMachine");
- idx = childIndex(t, scxml->children);
- } else {
- Q_UNREACHABLE();
+ if (line.length() + nr.length() + 1 > maxLineLength) {
+ out += line + QLatin1Char('\n');
+ line.clear();
+ } else if (i != 0) {
+ line += QLatin1Char(' ');
}
- return QStringLiteral("transition_%1_%2").arg(parentName, QString::number(idx));
+ line += nr;
}
+ if (!line.isEmpty())
+ out += line;
+}
- static int childIndex(StateOrTransition *child, const QVector<StateOrTransition *> &children) {
- int idx = 0;
- foreach (StateOrTransition *sot, children) {
- if (sot == child)
- break;
+void generateTables(const GeneratedTableData &td, Replacements &replacements)
+{
+ { // instructions
+ auto instr = td.theInstructions;
+ QString out;
+ generateList(out, [&instr](int idx) -> QString {
+ if (instr.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("-1");
+ if (idx < instr.size())
+ return QString::number(instr.at(idx));
else
- ++idx;
- }
- return idx;
- }
-
- QString createList(const QString &elementType, const QStringList &elements) const
- { return createContainer(QStringLiteral("QList"), elementType, elements); }
-
- QString createVector(const QString &elementType, const QStringList &elements) const
- { return createContainer(QStringLiteral("QVector"), elementType, elements); }
-
- QString createContainer(const QString &baseType, const QString &elementType, const QStringList &elements) const
- {
- QString result;
- if (translationUnit->useCxx11) {
- if (elements.isEmpty()) {
- result += QStringLiteral("{}");
- } else {
- result += QStringLiteral("{ ") + elements.join(QStringLiteral(", ")) + QStringLiteral(" }");
- }
- } else {
- result += QStringLiteral("%1< %2 >()").arg(baseType, elementType);
- if (!elements.isEmpty()) {
- result += QStringLiteral(" << ") + elements.join(QStringLiteral(" << "));
- }
- }
- return result;
- }
-
- QString generateInitializer(AbstractState *node) const
- {
- QString init = mangledName(node, StateName) + QStringLiteral("(");
- if (State *parentState = node->parent->asState()) {
- init += QStringLiteral("&") + mangledName(parentState, StateName);
- } else {
- init += QStringLiteral("&stateMachine");
- }
- init += QLatin1Char(')');
- return init;
- }
-
- void addSubStateMachineProperties(ScxmlDocument *doc)
- {
- foreach (ScxmlDocument *subDocs, doc->allSubDocuments) {
- QString name = subDocs->root->name;
- if (name.isEmpty())
- continue;
- auto plainName = mangledName(name, PlainName);
- auto qualifiedName = namespacePrefix + plainName;
- if (m_serviceProps.contains(qMakePair(plainName, qualifiedName)))
- continue;
- m_serviceProps.append(qMakePair(name, qualifiedName));
- clazz.classFields << QStringLiteral("%1 *%2;").arg(qualifiedName, plainName);
- clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(plainName);
- if (isValidQPropertyName(name)) {
- clazz.properties << QStringLiteral("Q_PROPERTY(%1%2 *%3 READ %2 NOTIFY %4)")
- .arg(namespacePrefix, plainName, name,
- mangledName(name, SignalName));
- }
- if (m_qtMode) {
- Method getter(QStringLiteral("%1 *%2() const").arg(qualifiedName, plainName));
- getter.impl << QStringLiteral("%1 *%2::%3() const").arg(qualifiedName,
- clazz.className, plainName)
- << QStringLiteral("{ return data->%1; }").arg(plainName);
- clazz.publicMethods << getter;
- clazz.signalMethods << QStringLiteral("void %1(%2 *statemachine);")
- .arg(mangledName(name, SignalName), qualifiedName);
- }
-
- clazz.dataMethods << QStringLiteral("%1 *%2() const")
- .arg(qualifiedName, mangledName(name, MachineName))
- << QStringLiteral("{ return %1; }").arg(plainName)
- << QString();
- }
- }
-
- void addEvents()
- {
- QStringList knownEventsList = m_knownEvents.toList();
- std::sort(knownEventsList.begin(), knownEventsList.end());
- if (m_qtMode) {
- foreach (const QString &event, knownEventsList) {
- clazz.publicSlotDeclarations << QStringLiteral("void ") + event + QStringLiteral("(const QVariant &eventData = QVariant());");
- clazz.publicSlotDefinitions << QStringLiteral("void ") + clazz.className
- + QStringLiteral("::")
- + event
- + QStringLiteral("(const QVariant &eventData)\n{ submitEvent(data->") + qba(event)
- + QStringLiteral(", eventData); }");
- }
- }
-
- if (!m_signalNames.isEmpty()) {
- clazz.needsEventFilter = true;
- clazz.init.impl << QStringLiteral("stateMachine.setScxmlEventFilter(this);");
- auto &dm = clazz.dataMethods;
- dm << QStringLiteral("bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) Q_DECL_OVERRIDE {");
- if (m_qtMode) {
- dm << QStringLiteral(" if (event->originType() != QStringLiteral(\"qt:signal\")) { return true; }")
- << QStringLiteral(" %1 *m = static_cast< %1 * >(stateMachine);").arg(clazz.className);
- foreach (const QString &signalName, m_signalNames) {
- dm << QStringLiteral(" if (event->name() == %1) { emit m->%2(event->data()); return false; }")
- .arg(qba(signalName), mangleIdentifier(signalName));
- }
- }
- dm << QStringLiteral(" return true;")
- << QStringLiteral("}")
- << QString();
- }
- }
-
- QString createContextString(const QString &instrName) const Q_DECL_OVERRIDE
- {
- if (!m_currentTransitionName.isEmpty()) {
- QString state = parentStateName();
- return QStringLiteral("<%1> instruction in transition of state '%2'").arg(instrName, state);
- } else {
- return QStringLiteral("<%1> instruction in state '%2'").arg(instrName, parentStateName());
- }
- }
-
- QString createContext(const QString &instrName, const QString &attrName, const QString &attrValue) const Q_DECL_OVERRIDE
- {
- QString location = createContextString(instrName);
- return QStringLiteral("%1 with %2=\"%3\"").arg(location, attrName, attrValue);
- }
-
- QString parentStateName() const
- {
- for (int i = m_parents.size() - 1; i >= 0; --i) {
- Node *node = m_parents.at(i);
- if (State *s = node->asState())
- return s->id;
- else if (HistoryState *h = node->asHistoryState())
- return h->id;
- else if (Scxml *l = node->asScxml())
- return l->name;
- }
-
- return QString();
- }
-
- QString parentStateMemberName() const
- {
- Node *parent = m_parents.last();
- if (State *s = parent->asState())
- return mangledName(s, StateName);
- else if (HistoryState *h = parent->asHistoryState())
- return mangledName(h, StateName);
- else if (parent->asScxml())
- return QStringLiteral("stateMachine");
- else
- Q_UNIMPLEMENTED();
- return QString();
- }
-
- static void generateList(StringListDumper &t, std::function<QString(int)> next)
- {
- const int maxLineLength = 80;
- QString line;
- for (int i = 0; ; ++i) {
- QString nr = next(i);
- if (nr.isNull())
- break;
-
+ return QString();
+ });
+ replacements[QStringLiteral("theInstructions")] = out;
+ }
+
+ { // dataIds
+ auto dataIds = td.theDataNameIds;
+ QString out;
+ generateList(out, [&dataIds](int idx) -> QString {
+ if (dataIds.size() == 0 && idx == 0) // prevent generation of empty array
+ return QStringLiteral("-1");
+ if (idx < dataIds.size())
+ return QString::number(dataIds[idx]);
+ else
+ return QString();
+ });
+ replacements[QStringLiteral("dataNameCount")] = QString::number(dataIds.size());
+ replacements[QStringLiteral("dataIds")] = out;
+ }
+
+ { // evaluators
+ auto evaluators = td.theEvaluators;
+ QString out;
+ generateList(out, [&evaluators](int idx) -> QString {
+ if (evaluators.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("{ -1, -1 }");
+ if (idx >= evaluators.size())
+ return QString();
+
+ const auto eval = evaluators.at(idx);
+ return QStringLiteral("{ %1, %2 }").arg(eval.expr).arg(eval.context);
+ });
+ replacements[QStringLiteral("evaluatorCount")] = QString::number(evaluators.size());
+ replacements[QStringLiteral("evaluators")] = out;
+ }
+
+ { // assignments
+ auto assignments = td.theAssignments;
+ QString out;
+ generateList(out, [&assignments](int idx) -> QString {
+ if (assignments.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("{ -1, -1, -1 }");
+ if (idx >= assignments.size())
+ return QString();
+
+ auto assignment = assignments.at(idx);
+ return QStringLiteral("{ %1, %2, %3 }")
+ .arg(assignment.dest).arg(assignment.expr).arg(assignment.context);
+ });
+ replacements[QStringLiteral("assignmentCount")] = QString::number(assignments.size());
+ replacements[QStringLiteral("assignments")] = out;
+ }
+
+ { // foreaches
+ auto foreaches = td.theForeaches;
+ QString out;
+ generateList(out, [&foreaches](int idx) -> QString {
+ if (foreaches.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("{ -1, -1, -1, -1 }");
+ if (idx >= foreaches.size())
+ return QString();
+
+ auto foreachItem = foreaches.at(idx);
+ return QStringLiteral("{ %1, %2, %3, %4 }").arg(foreachItem.array).arg(foreachItem.item)
+ .arg(foreachItem.index).arg(foreachItem.context);
+ });
+ replacements[QStringLiteral("foreachCount")] = QString::number(foreaches.size());
+ replacements[QStringLiteral("foreaches")] = out;
+ }
+
+ { // strings
+ QString out;
+ auto strings = td.theStrings;
+ if (strings.isEmpty()) // prevent generation of empty array
+ strings.append(QStringLiteral(""));
+ int ucharCount = 0;
+ generateList(out, [&ucharCount, &strings](int idx) -> QString {
+ if (idx >= strings.size())
+ return QString();
+
+ const int length = strings.at(idx).size();
+ const QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg(
+ QString::number(idx), QString::number(ucharCount), QString::number(length));
+ ucharCount += length + 1;
+ return str;
+ });
+ replacements[QStringLiteral("stringCount")] = QString::number(strings.size());
+ replacements[QStringLiteral("strLits")] = out;
+
+ out.clear();
+ for (int i = 0, ei = strings.size(); i < ei; ++i) {
+ const QString &string = strings.at(i);
+ QString result;
if (i != 0)
- line += QLatin1Char(',');
-
- if (line.length() + nr.length() + 1 > maxLineLength) {
- t << line;
- line.clear();
- } else if (i != 0) {
- line += QLatin1Char(' ');
+ result += QLatin1Char('\n');
+ for (int charPos = 0, eCharPos = string.size(); charPos < eCharPos; ++charPos) {
+ result.append(QStringLiteral("0x%1,")
+ .arg(QString::number(string.at(charPos).unicode(), 16)));
}
- line += nr;
+ result.append(QStringLiteral("0%1 // %2: %3")
+ .arg(QLatin1String(i < ei - 1 ? "," : ""), QString::number(i),
+ cEscape(string)));
+ out += result;
}
- if (!line.isEmpty())
- t << line;
+ replacements[QStringLiteral("uniLits")] = out;
+ replacements[QStringLiteral("stringdataSize")] = QString::number(ucharCount + 1);
}
+}
- void generateTables()
- {
- StringListDumper &t = clazz.tables;
- clazz.classFields << QString();
- QScopedPointer<QScxmlExecutableContent::DynamicTableData> td(tableData());
-
- { // instructions
- clazz.classFields << QStringLiteral("static qint32 theInstructions[];");
- t << QStringLiteral("qint32 %1::Data::theInstructions[] = {").arg(clazz.className);
- auto instr = td->instructionTable();
- generateList(t, [&instr](int idx) -> QString {
- if (instr.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("-1");
- if (idx < instr.size())
- return QString::number(instr.at(idx));
- else
- return QString();
- });
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::Instructions instructions() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return theInstructions; }")
- << QString();
- }
-
- { // dataIds
- int count;
- auto dataIds = td->dataNames(&count);
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::StringId dataIds[];");
- t << QStringLiteral("QScxmlExecutableContent::StringId %1::Data::dataIds[] = {").arg(clazz.className);
- if (isCppDataModel()) {
- t << QStringLiteral("-1");
- } else {
- generateList(t, [&dataIds, count](int idx) -> QString {
- if (count == 0 && idx == 0) // prevent generation of empty array
- return QStringLiteral("-1");
- if (idx < count)
- return QString::number(dataIds[idx]);
- else
- return QString();
- });
- }
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::StringId *dataNames(int *count) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ *count = %1; return dataIds; }").arg(count);
- clazz.dataMethods << QString();
- }
-
- { // evaluators
- auto evaluators = td->evaluators();
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::EvaluatorInfo evaluators[];");
- t << QStringLiteral("QScxmlExecutableContent::EvaluatorInfo %1::Data::evaluators[] = {").arg(clazz.className);
- if (isCppDataModel()) {
- t << QStringLiteral("{ -1, -1 }");
- } else {
- generateList(t, [&evaluators](int idx) -> QString {
- if (evaluators.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("{ -1, -1 }");
- if (idx >= evaluators.size())
- return QString();
-
- auto eval = evaluators.at(idx);
- return QStringLiteral("{ %1, %2 }").arg(eval.expr).arg(eval.context);
- });
- }
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ Q_ASSERT(evaluatorId >= 0); Q_ASSERT(evaluatorId < %1); return evaluators[evaluatorId]; }").arg(evaluators.size());
- clazz.dataMethods << QString();
-
- if (isCppDataModel()) {
- {
- StringListDumper stringEvals;
- stringEvals << QStringLiteral("QString %1::evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = stringEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- stringEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" return [this]()->QString{ return %1; }();").arg(it.value());
- }
- stringEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" return QString();")
- << QStringLiteral(" }");
- stringEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(stringEvals));
- }
-
- {
- StringListDumper boolEvals;
- boolEvals << QStringLiteral("bool %1::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = boolEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- boolEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" return [this]()->bool{ return %1; }();").arg(it.value());
- }
- boolEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" return false;")
- << QStringLiteral(" }");
- boolEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(boolEvals));
- }
-
- {
- StringListDumper variantEvals;
- variantEvals << QStringLiteral("QVariant %1::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = variantEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- variantEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" return [this]()->QVariant{ return %1; }();").arg(it.value());
- }
- variantEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" return QVariant();")
- << QStringLiteral(" }");
- variantEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(variantEvals));
- }
-
- {
- StringListDumper voidEvals;
- voidEvals << QStringLiteral("void %1::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = voidEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- voidEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" [this]()->void{ %1 }();").arg(it.value())
- << QStringLiteral(" break;");
- }
- voidEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" }");
- voidEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(voidEvals));
- }
- }
- }
-
- { // assignments
- auto assignments = td->assignments();
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::AssignmentInfo assignments[];");
- t << QStringLiteral("QScxmlExecutableContent::AssignmentInfo %1::Data::assignments[] = {").arg(clazz.className);
- if (isCppDataModel()) {
- t << QStringLiteral("{ -1, -1, -1 }");
- } else {
- generateList(t, [&assignments](int idx) -> QString {
- if (assignments.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("{ -1, -1, -1 }");
- if (idx >= assignments.size())
- return QString();
-
- auto ass = assignments.at(idx);
- return QStringLiteral("{ %1, %2, %3 }").arg(ass.dest).arg(ass.expr).arg(ass.context);
- });
- }
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::AssignmentInfo assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ Q_ASSERT(assignmentId >= 0); Q_ASSERT(assignmentId < %1); return assignments[assignmentId]; }").arg(assignments.size());
- clazz.dataMethods << QString();
- }
-
- { // foreaches
- auto foreaches = td->foreaches();
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::ForeachInfo foreaches[];");
- t << QStringLiteral("QScxmlExecutableContent::ForeachInfo %1::Data::foreaches[] = {").arg(clazz.className);
- generateList(t, [&foreaches](int idx) -> QString {
- if (foreaches.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("{ -1, -1, -1, -1 }");
- if (idx >= foreaches.size())
- return QString();
-
- auto foreach = foreaches.at(idx);
- return QStringLiteral("{ %1, %2, %3, %4 }").arg(foreach.array).arg(foreach.item).arg(foreach.index).arg(foreach.context);
- });
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::ForeachInfo foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ Q_ASSERT(foreachId >= 0); Q_ASSERT(foreachId < %1); return foreaches[foreachId]; }").arg(foreaches.size());
- }
-
- { // strings
- t << QStringLiteral("#define STR_LIT(idx, ofs, len) \\")
- << QStringLiteral(" Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \\")
- << QStringLiteral(" qptrdiff(offsetof(Strings, stringdata) + ofs * sizeof(qunicodechar) - idx * sizeof(QArrayData)) \\")
- << QStringLiteral(" )");
-
- t << QStringLiteral("%1::Data::Strings %1::Data::strings = {{").arg(clazz.className);
- auto strings = td->stringTable();
- if (strings.isEmpty()) // prevent generation of empty array
- strings.append(QStringLiteral(""));
- int ucharCount = 0;
- generateList(t, [&ucharCount, &strings](int idx) -> QString {
- if (idx >= strings.size())
- return QString();
-
- int length = strings.at(idx).size();
- QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg(QString::number(idx),
- QString::number(ucharCount),
- QString::number(length));
- ucharCount += length + 1;
- return str;
- });
- t << QStringLiteral("},{");
- for (int i = 0, ei = strings.size(); i < ei; ++i) {
- const QString &string = strings.at(i);
- QString result;
- for (int charPos = 0, eCharPos = string.size(); charPos < eCharPos; ++charPos) {
- result.append(QStringLiteral("0x%1,")
- .arg(QString::number(string.at(charPos).unicode(), 16)));
- }
- result.append(QStringLiteral("0%1 // %2: %3")
- .arg(QLatin1String(i < ei - 1 ? "," : ""), QString::number(i),
- cEscape(string)));
- t << result;
- }
- t << QStringLiteral("}};") << QStringLiteral("");
-
- clazz.classFields << QStringLiteral("static struct Strings {")
- << QStringLiteral(" QArrayData data[%1];").arg(strings.size())
- << QStringLiteral(" qunicodechar stringdata[%1];").arg(ucharCount + 1)
- << QStringLiteral("} strings;");
-
- clazz.dataMethods << QStringLiteral("QString string(QScxmlExecutableContent::StringId id) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{");
- clazz.dataMethods << QStringLiteral(" Q_ASSERT(id >= QScxmlExecutableContent::NoString); Q_ASSERT(id < %1);").arg(strings.size());
- clazz.dataMethods << QStringLiteral(" if (id == QScxmlExecutableContent::NoString) return QString();");
- if (translationUnit->useCxx11) {
- clazz.dataMethods << QStringLiteral(" return QString({static_cast<QStringData*>(strings.data + id)});");
- } else {
- clazz.dataMethods << QStringLiteral(" QStringDataPtr data;");
- clazz.dataMethods << QStringLiteral(" data.ptr = static_cast<QStringData*>(strings.data + id);");
- clazz.dataMethods << QStringLiteral(" return QString(data);");
- }
- clazz.dataMethods << QStringLiteral("}");
- clazz.dataMethods << QString();
- }
- }
+void generateCppDataModelEvaluators(const GeneratedTableData::DataModelInfo &info,
+ Replacements &replacements)
+{
+ const QString switchStart = QStringLiteral(" switch (id) {\n");
+ const QString switchEnd = QStringLiteral(" default: break;\n }");
+ const QString unusedId = QStringLiteral(" Q_UNUSED(id);");
+ QString stringEvals;
+ if (!info.stringEvaluators.isEmpty()) {
+ stringEvals += switchStart;
+ for (auto it = info.stringEvaluators.constBegin(), eit = info.stringEvaluators.constEnd();
+ it != eit; ++it) {
+ stringEvals += QStringLiteral(" case %1:\n").arg(it.key());
+ stringEvals += QStringLiteral(" return [this]()->QString{ return %1; }();\n")
+ .arg(it.value());
+ }
+ stringEvals += switchEnd;
+ } else {
+ stringEvals += unusedId;
+ }
+ replacements[QStringLiteral("evaluateToStringCases")] = stringEvals;
+
+ QString boolEvals;
+ if (!info.boolEvaluators.isEmpty()) {
+ boolEvals += switchStart;
+ for (auto it = info.boolEvaluators.constBegin(), eit = info.boolEvaluators.constEnd();
+ it != eit; ++it) {
+ boolEvals += QStringLiteral(" case %1:\n").arg(it.key());
+ boolEvals += QStringLiteral(" return [this]()->bool{ return %1; }();\n")
+ .arg(it.value());
+ }
+ boolEvals += switchEnd;
+ } else {
+ boolEvals += unusedId;
+ }
+ replacements[QStringLiteral("evaluateToBoolCases")] = boolEvals;
+
+ QString variantEvals;
+ if (!info.variantEvaluators.isEmpty()) {
+ variantEvals += switchStart;
+ for (auto it = info.variantEvaluators.constBegin(), eit = info.variantEvaluators.constEnd();
+ it != eit; ++it) {
+ variantEvals += QStringLiteral(" case %1:\n").arg(it.key());
+ variantEvals += QStringLiteral(" return [this]()->QVariant{ return %1; }();\n")
+ .arg(it.value());
+ }
+ variantEvals += switchEnd;
+ } else {
+ variantEvals += unusedId;
+ }
+ replacements[QStringLiteral("evaluateToVariantCases")] = variantEvals;
+
+ QString voidEvals;
+ if (!info.voidEvaluators.isEmpty()) {
+ voidEvals = switchStart;
+ for (auto it = info.voidEvaluators.constBegin(), eit = info.voidEvaluators.constEnd();
+ it != eit; ++it) {
+ voidEvals += QStringLiteral(" case %1:\n").arg(it.key());
+ voidEvals += QStringLiteral(" [this]()->void{ %1 }();\n").arg(it.value());
+ voidEvals += QStringLiteral(" return;\n");
+ }
+ voidEvals += switchEnd;
+ } else {
+ voidEvals += unusedId;
+ }
+ replacements[QStringLiteral("evaluateToVoidCases")] = voidEvals;
+}
- void generateMetaObject()
+int createFactoryId(QStringList &factories, const QString &className,
+ const QString &namespacePrefix,
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+{
+ 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 >(")
+ .arg(namespacePrefix, className);
+ } else {
+ line += QStringLiteral("QScxmlDynamicScxmlServiceFactory(");
+ }
+ line += QStringLiteral("invoke(%1, %2, %3, %4, %5, %6, %7), ")
+ .arg(QString::number(invokeInfo.id),
+ QString::number(invokeInfo.prefix),
+ QString::number(invokeInfo.expr),
+ QString::number(invokeInfo.location),
+ QString::number(invokeInfo.context),
+ QString::number(invokeInfo.finalize))
+ .arg(invokeInfo.autoforward ? QStringLiteral("true") : QStringLiteral("false"));
{
- ClassDef classDef;
- classDef.classname = clazz.className.toUtf8();
- classDef.qualified = classDef.classname;
- classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public);
- classDef.hasQObject = true;
-
- // Event signals:
- foreach (const QString &signalName, m_signalNames) {
- FunctionDef signal;
- signal.type.name = "void";
- signal.type.rawName = signal.type.name;
- signal.normalizedType = signal.type.name;
- signal.name = signalName.toUtf8();
- signal.access = FunctionDef::Public;
- signal.isSignal = true;
-
- ArgumentDef arg;
- arg.type.name = "const QVariant &";
- arg.type.rawName = arg.type.name;
- arg.normalizedType = "QVariant";
- arg.name = "data";
- arg.typeNameForCast = arg.normalizedType + "*";
- signal.arguments << arg;
-
- classDef.signalList << signal;
- }
-
- // stateNames:
- foreach (const QString &stateName, m_stateNames) {
- if (stateName.isEmpty())
- continue;
-
- const QByteArray mangledStateName = mangledName(stateName, StateName).toUtf8();
- const QString mangledSignalName = mangledName(stateName, SignalName);
-
- FunctionDef signal;
- signal.type.name = "void";
- signal.type.rawName = signal.type.name;
- signal.normalizedType = signal.type.name;
- signal.name = mangledSignalName.toUtf8();
- signal.access = FunctionDef::Private;
- signal.isSignal = true;
- if (!m_qtMode) {
- signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);";
- } else {
- clazz.signalMethods << QStringLiteral("void %1(bool active);")
- .arg(mangledSignalName);
- }
- ArgumentDef arg;
- arg.type.name = "bool";
- arg.type.rawName = arg.type.name;
- arg.normalizedType = arg.type.name;
- arg.name = "active";
- arg.typeNameForCast = arg.type.name + "*";
- signal.arguments << arg;
- classDef.signalList << signal;
-
- ++classDef.notifyableProperties;
- PropertyDef prop;
- prop.name = stateName.toUtf8();
- prop.type = "bool";
- prop.read = "data->" + mangledStateName + ".active";
- prop.notify = mangledSignalName.toUtf8();
- prop.notifyId = classDef.signalList.size() - 1;
- prop.gspec = PropertyDef::ValueSpec;
- prop.scriptable = "true";
- classDef.propertyList << prop;
- }
-
- // event slots:
- foreach (const QString &eventName, m_knownEvents) {
- FunctionDef slot;
- slot.type.name = "void";
- slot.type.rawName = slot.type.name;
- slot.normalizedType = slot.type.name;
- slot.name = eventName.toUtf8();
- slot.access = FunctionDef::Public;
- slot.isSlot = true;
-
- classDef.slotList << slot;
-
- ArgumentDef arg;
- arg.type.name = "const QVariant &";
- arg.type.rawName = arg.type.name;
- arg.normalizedType = "QVariant";
- arg.name = "data";
- arg.typeNameForCast = arg.normalizedType + "*";
- slot.arguments << arg;
-
- classDef.slotList << slot;
+ QStringList l;
+ for (auto name : namelist) {
+ l.append(QString::number(name));
}
-
- // sub-statemachines:
- QHash<QByteArray, QByteArray> knownQObjectClasses;
- knownQObjectClasses.insert(QByteArray("QScxmlStateMachine"), QByteArray());
- Method reg(QStringLiteral("void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE Q_DECL_FINAL"));
- reg.impl << QStringLiteral("void %1::setService(const QString &id, QScxmlInvokableService *service) {").arg(clazz.className);
- if (m_serviceProps.isEmpty()) {
- reg.impl << QStringLiteral(" Q_UNUSED(id);")
- << QStringLiteral(" Q_UNUSED(service);");
- }
- for (const auto &service : m_serviceProps) {
- auto serviceName = service.first;
- const QString mangledServiceName = mangledName(serviceName, PlainName);
- const QString fqServiceClass = service.second;
- const QByteArray serviceClass = fqServiceClass.toUtf8();
- knownQObjectClasses.insert(serviceClass, "");
-
- reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %4)")
- .arg(addString(serviceName))
- .arg(mangledServiceName, namespacePrefix).arg(classDef.signalList.size());
-
- const QByteArray mangledMachineName = mangledName(serviceName, MachineName).toUtf8();
- const QByteArray mangledSignalName = mangledName(serviceName, SignalName).toUtf8();
-
- FunctionDef signal;
- signal.type.name = "void";
- signal.type.rawName = signal.type.name;
- signal.normalizedType = signal.type.name;
- signal.name = mangledSignalName;
- signal.access = FunctionDef::Private;
- signal.isSignal = true;
- if (!m_qtMode) {
- signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);";
- }
- ArgumentDef arg;
- arg.type.name = serviceClass + " *";
- arg.type.rawName = arg.type.name;
- arg.type.referenceType = Type::Pointer;
- arg.normalizedType = serviceClass + "*(*)";
- arg.name = "statemachine";
- arg.typeNameForCast = arg.type.name + "*";
- signal.arguments << arg;
- classDef.signalList << signal;
-
- ++classDef.notifyableProperties;
- PropertyDef prop;
- prop.name = serviceName.toUtf8();
- prop.type = serviceClass + "*";
- prop.read = "data->" + mangledMachineName;
- prop.notify = mangledSignalName;
- prop.notifyId = classDef.signalList.size() - 1;
- prop.gspec = PropertyDef::ValueSpec;
- prop.scriptable = "true";
- classDef.propertyList << prop;
- }
- reg.impl << QStringLiteral("}");
- clazz.protectedMethods.append(reg);
-
- QBuffer buf(&clazz.metaData);
- buf.open(QIODevice::WriteOnly);
- Generator(&classDef, QList<QByteArray>(), knownQObjectClasses,
- QHash<QByteArray, QByteArray>(), buf).generateCode();
- buf.close();
- }
-
- QString qba(const QString &bytes)
- {
- return QStringLiteral("string(%1)").arg(addString(bytes));
- }
-
- QString scxmlClassName(DocumentModel::ScxmlDocument *doc)
- {
- QString name = mangleIdentifier(translationUnit->classnameForDocument.value(doc));
- Q_ASSERT(!name.isEmpty());
- return namespacePrefix + name;
+ line += QStringLiteral("%1, ").arg(createContainer(l));
}
-
- /*!
- * \internal
- * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers
- * are replaced by the pattern \c _0x<hex>_ where <hex> is the hexadecimal unicode
- * representation of the character. As identifiers with leading underscores followed by either
- * another underscore or a capital letter are reserved in C++, we also escape those, by escaping
- * the first underscore, using the above method.
- *
- * We keep track of all identifiers we have used so far and if we find two different names that
- * map to the same mangled identifier by the above method, we append underscores to the new one
- * until the result is unique.
- *
- * \note
- * Although C++11 allows for non-ascii (unicode) characters to be used in identifiers,
- * many compilers forgot to read the spec and do not implement this. Some also do not
- * implement C99 identifiers, because that is \e {at the implementation's discretion}. So,
- * we are stuck with plain old boring identifiers.
- */
- QString mangleIdentifier(const QString &str) const
{
- auto isNonDigit = [](QChar c) -> bool {
- return (c >= QLatin1Char('a') && c <= QLatin1Char('z')) ||
- (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) ||
- c == QLatin1Char('_');
- };
-
- Q_ASSERT(!str.isEmpty());
-
- QString mangled;
- mangled.reserve(str.size());
-
- int i = 0;
- if (str.startsWith(QLatin1Char('_')) && str.size() > 1) {
- QChar ch = str.at(1);
- if (ch == QLatin1Char('_')
- || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) {
- mangled += QLatin1String("_0x5f_");
- ++i;
- }
- }
-
- for (int ei = str.length(); i != ei; ++i) {
- auto c = str.at(i).unicode();
- if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) {
- mangled += c;
- } else {
- mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');
- }
+ QStringList l;
+ for (const auto &parameter : parameters) {
+ l += QStringLiteral("param(%1, %2, %3)")
+ .arg(QString::number(parameter.name),
+ QString::number(parameter.expr),
+ QString::number(parameter.location));
}
-
- while (true) {
- auto it = m_mangledToOriginal.constFind(mangled);
- if (it == m_mangledToOriginal.constEnd()) {
- m_mangledToOriginal.insert(mangled, str);
- break;
- } else if (it.value() == str) {
- break;
- }
- mangled += QStringLiteral("_"); // append underscores until we get a unique name
- }
-
- return mangled;
+ line += QStringLiteral("%1);").arg(createContainer(l));
}
-private:
- QString namespacePrefix;
- ClassDump &clazz;
- TranslationUnit *translationUnit;
- mutable QHash<AbstractState *, QHash<NameForm, QString> > m_mangledNames;
- mutable QHash<QString, QString> m_mangledToOriginal;
- QVector<Node *> m_parents;
- QList<QPair<QString, QString>> m_serviceProps;
- QSet<QString> m_knownEvents;
- QSet<QString> m_signals;
- QStringList m_signalNames;
- QStringList m_stateNames;
- QStringList m_stateFieldNames;
- QString m_currentTransitionName;
- bool m_bindLate;
- bool m_qtMode;
- QVector<DocumentModel::DataElement *> m_dataElements;
-};
+ factories.append(line);
+ return idx;
+}
} // anonymous namespace
-
void CppDumper::dump(TranslationUnit *unit)
{
Q_ASSERT(unit);
@@ -1313,38 +418,73 @@ void CppDumper::dump(TranslationUnit *unit)
m_translationUnit = unit;
- QStringList classDecls;
- QVector<ClassDump> clazzes;
- auto docs = m_translationUnit->otherDocuments();
- clazzes.resize(docs.size() + 1);
- DumperVisitor(clazzes[0], m_translationUnit).process(unit->mainDocument);
+ QString namespacePrefix;
+ if (!m_translationUnit->namespaceName.isEmpty()) {
+ namespacePrefix = QStringLiteral("::%1").arg(m_translationUnit->namespaceName);
+ }
+
+ QStringList classNames;
+ QVector<GeneratedTableData> tables;
+ QVector<GeneratedTableData::MetaDataInfo> metaDataInfos;
+ QVector<GeneratedTableData::DataModelInfo> dataModelInfos;
+ QVector<QStringList> factories;
+ auto docs = m_translationUnit->allDocuments;
+ tables.resize(docs.size());
+ metaDataInfos.resize(tables.size());
+ dataModelInfos.resize(tables.size());
+ factories.resize(tables.size());
+ auto classnameForDocument = m_translationUnit->classnameForDocument;
+
for (int i = 0, ei = docs.size(); i != ei; ++i) {
auto doc = docs.at(i);
- ClassDump &clazz = clazzes[i + 1];
- DumperVisitor(clazz, m_translationUnit).process(doc);
- classDecls.append(clazz.className);
+ auto metaDataInfo = &metaDataInfos[i];
+ GeneratedTableData::build(doc, &tables[i], metaDataInfo, &dataModelInfos[i],
+ [this, &factories, i, &classnameForDocument, &namespacePrefix](
+ const QScxmlExecutableContent::InvokeInfo &invokeInfo,
+ const QVector<QScxmlExecutableContent::StringId> &names,
+ const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int {
+ QString className;
+ if (invokeInfo.expr == QScxmlExecutableContent::NoInstruction) {
+ className = mangleIdentifier(classnameForDocument.value(content.data()));
+ }
+ return createFactoryId(factories[i], className, namespacePrefix,
+ invokeInfo, names, parameters);
+ });
+ classNames.append(mangleIdentifier(classnameForDocument.value(doc)));
}
- QString headerName = QFileInfo(unit->outHFileName).fileName();
+ const QString headerName = QFileInfo(m_translationUnit->outHFileName).fileName();
const QString headerGuard = headerName.toUpper()
.replace(QLatin1Char('.'), QLatin1Char('_'))
.replace(QLatin1Char('-'), QLatin1Char('_'));
- writeHeaderStart(headerGuard, classDecls);
- writeImplStart(clazzes);
+ const QStringList forwardDecls = classNames.mid(1);
+ writeHeaderStart(headerGuard, forwardDecls);
+ writeImplStart();
+
+ for (int i = 0, ei = tables.size(); i != ei; ++i) {
+ const GeneratedTableData &table = tables.at(i);
+ DocumentModel::ScxmlDocument *doc = docs.at(i);
+ writeClass(classNames.at(i), metaDataInfos.at(i));
+ writeImplBody(table, classNames.at(i), doc, factories.at(i), metaDataInfos.at(i));
- foreach (const ClassDump &clazz, clazzes) {
- writeClass(clazz);
- writeImplBody(clazz);
+ if (doc->root->dataModel == DocumentModel::Scxml::CppDataModel) {
+ Replacements r;
+ r[QStringLiteral("datamodel")] = doc->root->cppDataModelClassName;
+ generateCppDataModelEvaluators(dataModelInfos.at(i), r);
+ genTemplate(cpp, QStringLiteral(":/cppdatamodel.t"), r);
+ }
}
- classDecls.append(clazzes.at(0).className);
- writeHeaderEnd(headerGuard, classDecls);
+ writeHeaderEnd(headerGuard, classNames);
writeImplEnd();
}
void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList &forwardDecls)
{
- h << doNotEditComment.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR))
+ h << doNotEditComment.arg(m_translationUnit->scxmlFileName,
+ QString::number(Q_QSCXMLC_OUTPUT_REVISION),
+ QString::fromLatin1(QT_VERSION_STR))
<< endl;
h << QStringLiteral("#ifndef ") << headerGuard << endl
@@ -1355,59 +495,18 @@ void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList &
h << l("namespace ") << m_translationUnit->namespaceName << l(" {") << endl << endl;
if (!forwardDecls.isEmpty()) {
- foreach (const QString &name, forwardDecls) {
- h << QStringLiteral("class %1;").arg(name) << endl;
- }
+ for (const QString &forwardDecl : forwardDecls)
+ h << QStringLiteral("class %1;").arg(forwardDecl) << endl;
h << endl;
}
}
-void CppDumper::writeClass(const ClassDump &clazz)
+void CppDumper::writeClass(const QString &className, const GeneratedTableData::MetaDataInfo &info)
{
- h << l("class ") << clazz.className << QStringLiteral(": public QScxmlStateMachine\n{") << endl;
- h << QStringLiteral("public:") << endl
- << QStringLiteral(" /* qmake ignore Q_OBJECT */") << endl
- << QStringLiteral(" Q_OBJECT") << endl;
- clazz.properties.write(h, QStringLiteral(" "), QStringLiteral("\n"));
-
- h << endl
- << QStringLiteral("public:") << endl;
- h << l(" ") << clazz.className << l("(QObject *parent = 0);") << endl;
- h << l(" ~") << clazz.className << "();" << endl;
-
- if (!clazz.publicMethods.isEmpty()) {
- h << endl;
- foreach (const Method &m, clazz.publicMethods) {
- h << QStringLiteral(" ") << m.decl << QLatin1Char(';') << endl;
- }
- }
-
- if (!clazz.protectedMethods.isEmpty()) {
- h << endl
- << QStringLiteral("protected:") << endl;
- foreach (const Method &m, clazz.protectedMethods) {
- h << QStringLiteral(" ") << m.decl << QLatin1Char(';') << endl;
- }
- }
-
- if (!clazz.signalMethods.isEmpty()) {
- h << endl
- << QStringLiteral("signals:") << endl;
- clazz.signalMethods.write(h, QStringLiteral(" "), QStringLiteral("\n"));
- }
-
- if (!clazz.publicSlotDeclarations.isEmpty()) {
- h << endl
- << QStringLiteral("public slots:") << endl;
- clazz.publicSlotDeclarations.write(h, QStringLiteral(" "), QStringLiteral("\n"));
- }
-
- h << endl
- << l("private:") << endl
- << l(" struct Data;") << endl
- << l(" friend struct Data;") << endl
- << l(" struct Data *data;") << endl
- << l("};") << endl << endl;
+ Replacements r;
+ r[QStringLiteral("classname")] = className;
+ r[QStringLiteral("properties")] = generatePropertyDecls(info);
+ genTemplate(h, QStringLiteral(":/decl.t"), r);
}
void CppDumper::writeHeaderEnd(const QString &headerGuard, const QStringList &metatypeDecls)
@@ -1419,129 +518,251 @@ void CppDumper::writeHeaderEnd(const QString &headerGuard, const QStringList &me
ns = QStringLiteral("::%1").arg(m_translationUnit->namespaceName);
}
- foreach (const QString &name, metatypeDecls) {
- h << QStringLiteral("Q_DECLARE_METATYPE(%1::%2*);").arg(ns, name) << endl;
+ for (const QString &name : metatypeDecls) {
+ h << QStringLiteral("Q_DECLARE_METATYPE(%1::%2*)").arg(ns, name) << endl;
}
h << endl;
h << QStringLiteral("#endif // ") << headerGuard << endl;
}
-void CppDumper::writeImplStart(const QVector<ClassDump> &allClazzes)
+void CppDumper::writeImplStart()
{
- cpp << doNotEditComment.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR))
+ cpp << doNotEditComment.arg(m_translationUnit->scxmlFileName,
+ QString::number(Q_QSCXMLC_OUTPUT_REVISION),
+ l(QT_VERSION_STR))
<< endl;
- StringListDumper includes;
- foreach (const ClassDump &clazz, allClazzes) {
- includes.text += clazz.implIncludes.text;
+ QStringList includes;
+ for (DocumentModel::ScxmlDocument *doc : qAsConst(m_translationUnit->allDocuments)) {
+ switch (doc->root->dataModel) {
+ case DocumentModel::Scxml::NullDataModel:
+ includes += l("QScxmlNullDataModel");
+ break;
+ case DocumentModel::Scxml::JSDataModel:
+ includes += l("QScxmlEcmaScriptDataModel");
+ break;
+ case DocumentModel::Scxml::CppDataModel:
+ includes += doc->root->cppDataModelHeaderName;
+ break;
+ }
+
}
- includes.unique();
+ includes.sort();
+ includes.removeDuplicates();
QString headerName = QFileInfo(m_translationUnit->outHFileName).fileName();
cpp << l("#include \"") << headerName << l("\"") << endl;
cpp << endl
- << QStringLiteral("#include <qscxmlqstates.h>") << endl
+ << QStringLiteral("#include <qscxmlinvokableservice.h>") << endl
<< QStringLiteral("#include <qscxmltabledata.h>") << endl;
- if (!includes.isEmpty()) {
- includes.write(cpp, QStringLiteral("#include <"), QStringLiteral(">\n"));
- cpp << endl;
+ for (const QString &inc : qAsConst(includes)) {
+ cpp << l("#include <") << inc << l(">") << endl;
}
cpp << endl
- << revisionCheck.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR))
+ << revisionCheck.arg(m_translationUnit->scxmlFileName,
+ QString::number(Q_QSCXMLC_OUTPUT_REVISION),
+ QString::fromLatin1(QT_VERSION_STR))
<< endl;
if (!m_translationUnit->namespaceName.isEmpty())
cpp << l("namespace ") << m_translationUnit->namespaceName << l(" {") << endl << endl;
}
-void CppDumper::writeImplBody(const ClassDump &clazz)
+void CppDumper::writeImplBody(const GeneratedTableData &table,
+ const QString &className,
+ DocumentModel::ScxmlDocument *doc,
+ const QStringList &factory,
+ const GeneratedTableData::MetaDataInfo &info)
+{
+ QString dataModelField, dataModelInitialization;
+ switch (doc->root->dataModel) {
+ case DocumentModel::Scxml::NullDataModel:
+ dataModelField = l("QScxmlNullDataModel dataModel;");
+ dataModelInitialization = l("stateMachine.setDataModel(&dataModel);");
+ break;
+ case DocumentModel::Scxml::JSDataModel:
+ dataModelField = l("QScxmlEcmaScriptDataModel dataModel;");
+ dataModelInitialization = l("stateMachine.setDataModel(&dataModel);");
+ break;
+ case DocumentModel::Scxml::CppDataModel:
+ dataModelField = QStringLiteral("// Data model %1 is set from outside.").arg(
+ doc->root->cppDataModelClassName);
+ dataModelInitialization = dataModelField;
+ break;
+ }
+
+ QString name;
+ if (table.theName == -1) {
+ name = QStringLiteral("QString()");
+ } else {
+ name = QStringLiteral("string(%1)").arg(table.theName);
+ }
+
+ QString serviceFactories;
+ if (factory.isEmpty()) {
+ serviceFactories = QStringLiteral(" Q_UNUSED(id);\n Q_UNREACHABLE();");
+ } else {
+ serviceFactories = QStringLiteral(" switch (id) {\n ")
+ + factory.join(QStringLiteral("\n "))
+ + QStringLiteral("\n default: Q_UNREACHABLE();\n }");
+ }
+
+
+ Replacements r;
+ r[QStringLiteral("classname")] = className;
+ r[QStringLiteral("name")] = name;
+ r[QStringLiteral("initialSetup")] = QString::number(table.initialSetup());
+ generateTables(table, r);
+ r[QStringLiteral("dataModelField")] = dataModelField;
+ r[QStringLiteral("dataModelInitialization")] = dataModelInitialization;
+ r[QStringLiteral("theStateMachineTable")] =
+ GeneratedTableData::toString(table.stateMachineTable());
+ r[QStringLiteral("metaObject")] = generateMetaObject(className, info);
+ r[QStringLiteral("serviceFactories")] = serviceFactories;
+ genTemplate(cpp, QStringLiteral(":/data.t"), r);
+}
+
+void CppDumper::writeImplEnd()
{
- cpp << l("struct ") << clazz.className << l("::Data: private QScxmlTableData");
- if (clazz.needsEventFilter) {
- cpp << QStringLiteral(", public QScxmlEventFilter");
+ if (!m_translationUnit->namespaceName.isEmpty()) {
+ cpp << endl
+ << QStringLiteral("} // %1 namespace").arg(m_translationUnit->namespaceName) << endl;
}
- cpp << l(" {") << endl;
-
- cpp << QStringLiteral(" Data(%1 &stateMachine)").arg(clazz.className) << endl
- << QStringLiteral(" : stateMachine(stateMachine)") << endl;
- clazz.constructor.initializer.write(cpp, QStringLiteral(" , "), QStringLiteral("\n"));
- cpp << l(" {") << endl;
- clazz.constructor.impl.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
- cpp << l(" }") << endl;
-
- cpp << endl;
- cpp << l(" void init() {\n");
- clazz.init.impl.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
- cpp << l(" }") << endl;
- cpp << endl;
- clazz.dataMethods.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
+}
- cpp << endl
- << QStringLiteral(" %1 &stateMachine;").arg(clazz.className) << endl;
- clazz.classFields.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
+/*!
+ * \internal
+ * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers
+ * are replaced by the pattern \c _0x<hex>_ where <hex> is the hexadecimal unicode
+ * representation of the character. As identifiers with leading underscores followed by either
+ * another underscore or a capital letter are reserved in C++, we also escape those, by escaping
+ * the first underscore, using the above method.
+ *
+ * We keep track of all identifiers we have used so far and if we find two different names that
+ * map to the same mangled identifier by the above method, we append underscores to the new one
+ * until the result is unique.
+ *
+ * \note
+ * Although C++11 allows for non-ascii (unicode) characters to be used in identifiers,
+ * many compilers forgot to read the spec and do not implement this. Some also do not
+ * implement C99 identifiers, because that is \e {at the implementation's discretion}. So,
+ * we are stuck with plain old boring identifiers.
+ */
+QString CppDumper::mangleIdentifier(const QString &str)
+{
+ auto isNonDigit = [](QChar c) -> bool {
+ return (c >= QLatin1Char('a') && c <= QLatin1Char('z')) ||
+ (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) ||
+ c == QLatin1Char('_');
+ };
- cpp << l("};") << endl
- << endl;
- clazz.classMethods.write(cpp, QStringLiteral(""), QStringLiteral("\n"));
+ Q_ASSERT(!str.isEmpty());
- cpp << clazz.className << l("::") << clazz.className << l("(QObject *parent)") << endl
- << QStringLiteral(" : QScxmlStateMachine(parent)") << endl
- << QStringLiteral(" , data(new Data(*this))") << endl
- << QStringLiteral("{ qRegisterMetaType< %1 * >(); data->init(); }").arg(clazz.className) << endl
- << endl;
- cpp << clazz.className << l("::~") << clazz.className << l("()") << endl
- << l("{ delete data; }") << endl
- << endl;
- foreach (const Method &m, clazz.publicMethods) {
- m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n"), clazz.className);
- cpp << endl;
- }
- if (!clazz.protectedMethods.isEmpty()) {
- cpp << "#define SET_SERVICE_PROP(s, n, fq, sig) \\\n"
- " if (id == data->string(s)) { \\\n"
- " QScxmlInvokableScxml *machine = service ? dynamic_cast<QScxmlInvokableScxml *>(service) : Q_NULLPTR; \\\n"
- " fq *casted = machine ? dynamic_cast<fq*>(machine->stateMachine()) : Q_NULLPTR; \\\n"
- " if (data->n != casted) { \\\n"
- " data->n = casted; \\\n"
- " void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&casted)) }; \\\n"
- " QMetaObject::activate(this, &staticMetaObject, sig, _a); \\\n"
- " } \\\n"
- " return; \\\n"
- " }\n"
- << endl;
- foreach (const Method &m, clazz.protectedMethods) {
- m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n"), clazz.className);
- cpp << endl;
+ QString mangled;
+ mangled.reserve(str.size());
+
+ int i = 0;
+ if (str.startsWith(QLatin1Char('_')) && str.size() > 1) {
+ QChar ch = str.at(1);
+ if (ch == QLatin1Char('_')
+ || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) {
+ mangled += QLatin1String("_0x5f_");
+ ++i;
}
- cpp << QStringLiteral("#undef SET_SERVICE_PROP") << endl
- << endl;
}
- clazz.publicSlotDefinitions.write(cpp, QStringLiteral("\n"), QStringLiteral("\n"));
- cpp << endl;
- clazz.tables.write(cpp, QStringLiteral(""), QStringLiteral("\n"));
+ for (int ei = str.length(); i != ei; ++i) {
+ auto c = str.at(i).unicode();
+ if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) {
+ mangled += c;
+ } else {
+ mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');
+ }
+ }
- if (!clazz.dataModelMethods.isEmpty()) {
- bool first = true;
- foreach (const Method &m, clazz.dataModelMethods) {
- if (first) {
- first = false;
- } else {
- cpp << endl;
- }
- m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n"));
+ while (true) {
+ auto it = m_mangledToOriginal.constFind(mangled);
+ if (it == m_mangledToOriginal.constEnd()) {
+ m_mangledToOriginal.insert(mangled, str);
+ break;
+ } else if (it.value() == str) {
+ break;
}
+ mangled += QStringLiteral("_"); // append underscores until we get a unique name
}
- cpp << endl << clazz.metaData;
+ return mangled;
}
-void CppDumper::writeImplEnd()
+QString CppDumper::generatePropertyDecls(const GeneratedTableData::MetaDataInfo &info)
{
- if (!m_translationUnit->namespaceName.isEmpty()) {
- cpp << endl
- << QStringLiteral("} // %1 namespace").arg(m_translationUnit->namespaceName) << endl;
+ QString decls;
+
+ for (const QString &stateName : info.stateNames) {
+ if (!stateName.isEmpty())
+ decls += QString::fromLatin1(" Q_PROPERTY(bool %1)\n").arg(stateName);
}
+
+ return decls;
+}
+
+QString CppDumper::generateMetaObject(const QString &className,
+ const GeneratedTableData::MetaDataInfo &info)
+{
+ ClassDef classDef;
+ classDef.classname = className.toUtf8();
+ classDef.qualified = classDef.classname;
+ classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public);
+ classDef.hasQObject = true;
+
+ // stateNames:
+ int stateIdx = 0;
+ for (const QString &stateName : info.stateNames) {
+ if (stateName.isEmpty())
+ continue;
+
+ QByteArray mangledStateName = stateName.toUtf8();
+
+ FunctionDef signal;
+ signal.type.name = "void";
+ signal.type.rawName = signal.type.name;
+ signal.normalizedType = signal.type.name;
+ signal.name = mangledStateName + "Changed";
+ signal.access = FunctionDef::Public;
+ signal.isSignal = true;
+ signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);";
+
+ ArgumentDef arg;
+ arg.type.name = "bool";
+ arg.type.rawName = arg.type.name;
+ arg.normalizedType = arg.type.name;
+ arg.name = "active";
+ arg.typeNameForCast = arg.type.name + "*";
+ signal.arguments << arg;
+ classDef.signalList << signal;
+
+ ++classDef.notifyableProperties;
+ PropertyDef prop;
+ prop.name = stateName.toUtf8();
+ prop.type = "bool";
+ prop.read = "isActive(" + QByteArray::number(stateIdx++) + ")";
+ prop.notify = mangledStateName + "Changed";
+ prop.notifyId = classDef.signalList.size() - 1;
+ prop.gspec = PropertyDef::ValueSpec;
+ prop.scriptable = "true";
+ classDef.propertyList << prop;
+ }
+
+ // sub-statemachines:
+ QHash<QByteArray, QByteArray> knownQObjectClasses;
+ knownQObjectClasses.insert(QByteArray("QScxmlStateMachine"), QByteArray());
+
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ Generator(&classDef, QList<QByteArray>(), knownQObjectClasses,
+ QHash<QByteArray, QByteArray>(), buf).generateCode();
+ buf.close();
+ return QString::fromUtf8(buf.buffer());
}
QT_END_NAMESPACE
diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h
index f02bf2f..95fa48a 100644
--- a/tools/qscxmlc/scxmlcppdumper.h
+++ b/tools/qscxmlc/scxmlcppdumper.h
@@ -31,35 +31,26 @@
#include "qscxmlglobals.h"
-#include <QtScxml/private/qscxmlparser_p.h>
+#include <QtScxml/private/qscxmlcompiler_p.h>
+#include <QtScxml/private/qscxmltabledata_p.h>
#include <QTextStream>
QT_BEGIN_NAMESPACE
-struct ClassDump;
-
struct TranslationUnit
{
TranslationUnit()
- : useCxx11(true)
- , mainDocument(Q_NULLPTR)
+ : mainDocument(Q_NULLPTR)
{}
QString scxmlFileName;
QString outHFileName, outCppFileName;
QString namespaceName;
- bool useCxx11;
DocumentModel::ScxmlDocument *mainDocument;
+ QList<DocumentModel::ScxmlDocument *> allDocuments;
QHash<DocumentModel::ScxmlDocument *, QString> classnameForDocument;
QList<TranslationUnit *> dependencies;
-
- QList<DocumentModel::ScxmlDocument *> otherDocuments() const
- {
- auto docs = classnameForDocument.keys();
- docs.removeOne(mainDocument);
- return docs;
- }
};
class CppDumper
@@ -74,13 +65,23 @@ public:
private:
void writeHeaderStart(const QString &headerGuard, const QStringList &forwardDecls);
- void writeClass(const ClassDump &clazz);
+ void writeClass(const QString &className,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
void writeHeaderEnd(const QString &headerGuard, const QStringList &metatypeDecls);
- void writeImplStart(const QVector<ClassDump> &allClazzes);
- void writeImplBody(const ClassDump &clazz);
+ void writeImplStart();
+ void writeImplBody(const QScxmlInternal::GeneratedTableData &table,
+ const QString &className,
+ DocumentModel::ScxmlDocument *doc,
+ const QStringList &factory,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
void writeImplEnd();
+ QString mangleIdentifier(const QString &str);
private:
+ QString generatePropertyDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+ QString generateMetaObject(const QString &className,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+
QTextStream &h;
QTextStream &cpp;
@@ -88,6 +89,8 @@ private:
static QLatin1String l (const char *str) { return QLatin1String(str); }
TranslationUnit *m_translationUnit;
+
+ mutable QHash<QString, QString> m_mangledToOriginal;
};
QT_END_NAMESPACE
diff --git a/tools/qscxmlc/templates.qrc b/tools/qscxmlc/templates.qrc
new file mode 100644
index 0000000..6f2ccf6
--- /dev/null
+++ b/tools/qscxmlc/templates.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data.t</file>
+ <file>decl.t</file>
+ <file>cppdatamodel.t</file>
+ </qresource>
+</RCC>