1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "statemachineloader_p.h"
5
6#include <QtScxml/qscxmlstatemachine.h>
7#include <qqmlcontext.h>
8#include <qqmlengine.h>
9#include <qqmlinfo.h>
10#include <qqmlfile.h>
11#include <qbuffer.h>
12
13/*!
14 \qmltype StateMachineLoader
15//! \nativetype QScxmlStateMachineLoader
16 \inqmlmodule QtScxml
17
18 \brief Dynamically loads an SCXML document and instantiates the state machine.
19
20 \since QtScxml 5.7
21 */
22
23QScxmlStateMachineLoader::QScxmlStateMachineLoader(QObject *parent)
24 : QObject(parent)
25 , m_implicitDataModel(nullptr)
26{
27}
28
29/*!
30 \qmlproperty ScxmlStateMachine StateMachineLoader::stateMachine
31
32 The state machine instance.
33 */
34QT_PREPEND_NAMESPACE(QScxmlStateMachine) *QScxmlStateMachineLoader::stateMachine() const
35{
36 return m_stateMachine;
37}
38
39void QScxmlStateMachineLoader::setStateMachine(QScxmlStateMachine* stateMachine)
40{
41 if (m_stateMachine.valueBypassingBindings() != stateMachine) {
42 delete m_stateMachine.valueBypassingBindings();
43 m_stateMachine.setValueBypassingBindings(stateMachine);
44 }
45}
46
47QBindable<QScxmlStateMachine*> QScxmlStateMachineLoader::bindableStateMachine()
48{
49 return &m_stateMachine;
50}
51
52/*!
53 \qmlproperty url StateMachineLoader::source
54
55 The URL of the SCXML document to load. Only synchronously accessible URLs
56 are supported.
57 */
58QUrl QScxmlStateMachineLoader::source()
59{
60 return m_source;
61}
62
63void QScxmlStateMachineLoader::setSource(const QUrl &source)
64{
65 if (!source.isValid())
66 return;
67
68 m_source.removeBindingUnlessInWrapper();
69
70 const QUrl oldSource = m_source.valueBypassingBindings();
71 const auto *oldStateMachine = m_stateMachine.valueBypassingBindings();
72 setStateMachine(nullptr);
73 m_implicitDataModel = nullptr;
74
75 if (parse(source))
76 m_source.setValueBypassingBindings(source);
77 else
78 m_source.setValueBypassingBindings(QUrl());
79
80 if (oldSource != m_source.valueBypassingBindings())
81 m_source.notify();
82
83 if (oldStateMachine != m_stateMachine.valueBypassingBindings())
84 m_stateMachine.notify();
85}
86
87QBindable<QUrl> QScxmlStateMachineLoader::bindableSource()
88{
89 return &m_source;
90}
91
92QVariantMap QScxmlStateMachineLoader::initialValues() const
93{
94 return m_initialValues;
95}
96
97void QScxmlStateMachineLoader::setInitialValues(const QVariantMap &initialValues)
98{
99 m_initialValues.removeBindingUnlessInWrapper();
100 if (initialValues == m_initialValues.valueBypassingBindings())
101 return;
102
103 m_initialValues.setValueBypassingBindings(initialValues);
104
105 const auto stateMachine = m_stateMachine.valueBypassingBindings();
106 if (stateMachine)
107 stateMachine->setInitialValues(initialValues);
108 m_initialValues.notify();
109}
110
111QBindable<QVariantMap> QScxmlStateMachineLoader::bindableInitialValues()
112{
113 return &m_initialValues;
114}
115
116QScxmlDataModel *QScxmlStateMachineLoader::dataModel() const
117{
118 return m_dataModel;
119}
120
121void QScxmlStateMachineLoader::setDataModel(QScxmlDataModel *dataModel)
122{
123 m_dataModel.removeBindingUnlessInWrapper();
124 if (dataModel == m_dataModel.valueBypassingBindings()) {
125 return;
126 }
127 m_dataModel.setValueBypassingBindings(dataModel);
128 const auto stateMachine = m_stateMachine.valueBypassingBindings();
129 if (stateMachine) {
130 if (dataModel)
131 stateMachine->setDataModel(dataModel);
132 else
133 stateMachine->setDataModel(m_implicitDataModel);
134 }
135 m_dataModel.notify();
136}
137
138QBindable<QScxmlDataModel*> QScxmlStateMachineLoader::bindableDataModel()
139{
140 return &m_dataModel;
141}
142
143bool QScxmlStateMachineLoader::parse(const QUrl &source)
144{
145 if (!QQmlFile::isSynchronous(url: source)) {
146 qmlWarning(me: this) << QStringLiteral("Cannot open '%1' for reading: only synchronous access is supported.")
147 .arg(a: source.url());
148 return false;
149 }
150 QQmlFile scxmlFile(QQmlEngine::contextForObject(this)->engine(), source);
151 if (scxmlFile.isError()) {
152 // the synchronous case can only fail when the file is not found (or not readable).
153 qmlWarning(me: this) << QStringLiteral("Cannot open '%1' for reading.").arg(a: source.url());
154 return false;
155 }
156
157 QByteArray data(scxmlFile.dataByteArray());
158 QBuffer buf(&data);
159 if (!buf.open(openMode: QIODevice::ReadOnly)) {
160 qmlWarning(me: this) << QStringLiteral("Cannot open input buffer for reading");
161 return false;
162 }
163
164 QString fileName;
165 if (source.isLocalFile()) {
166 fileName = source.toLocalFile();
167 } else if (source.scheme() == QStringLiteral("qrc")) {
168 fileName = QStringLiteral(":") + source.path();
169 } else {
170 qmlWarning(me: this) << QStringLiteral("%1 is neither a local nor a resource URL.")
171 .arg(a: source.url())
172 << QStringLiteral("Invoking services by relative path will not work.");
173 }
174
175 auto stateMachine = QScxmlStateMachine::fromData(data: &buf, fileName);
176 stateMachine->setParent(this);
177 m_implicitDataModel = stateMachine->dataModel();
178
179 if (stateMachine->parseErrors().isEmpty()) {
180 if (m_dataModel.value())
181 stateMachine->setDataModel(m_dataModel.value());
182 stateMachine->setInitialValues(m_initialValues.value());
183 setStateMachine(stateMachine);
184 // as this is deferred any pending property updates to m_dataModel and m_initialValues
185 // should still occur before start().
186 QMetaObject::invokeMethod(obj: m_stateMachine.valueBypassingBindings(), member: "start", c: Qt::QueuedConnection);
187 return true;
188 } else {
189 qmlWarning(me: this) << QStringLiteral("Something went wrong while parsing '%1':")
190 .arg(a: source.url())
191 << Qt::endl;
192 const auto errors = stateMachine->parseErrors();
193 for (const QScxmlError &error : errors) {
194 qmlWarning(me: this) << error.toString();
195 }
196 return false;
197 }
198}
199

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtscxml/src/scxmlqml/statemachineloader.cpp