KDStateMachineEditor API Documentation 2.1
Loading...
Searching...
No Matches
qmlexporter.cpp
Go to the documentation of this file.
1/*
2 This file is part of the KDAB State Machine Editor Library.
3
4 SPDX-FileCopyrightText: 2014 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: Kevin Funk <kevin.funk@kdab.com>
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LicenseRef-KDAB-KDStateMachineEditor
8
9 Licensees holding valid commercial KDAB State Machine Editor Library
10 licenses may use this file in accordance with the KDAB State Machine Editor
11 Library License Agreement provided with the Software.
12
13 Contact info@kdab.com if any conditions of this licensing are not clear to you.
14*/
15
16#include "export/qmlexporter.h"
17
18#include "objecthelper.h"
19#include "element.h"
20#include "elementutil.h"
21#include "state.h"
22#include "transition.h"
23#include "debug.h"
24
25#include <QTextStream>
26
27#include <algorithm>
28
29using namespace KDSME;
30
31namespace {
32
33#define KDSME_QML_MODULE "QtQml.StateMachine"
34#define KDSME_QML_MODULE_VERSION "1.0"
35
36class LevelIncrementer // clazy:exclude=rule-of-three
37{
38public:
39 explicit LevelIncrementer(int *level)
40 : m_level(level)
41 {
42 ++(*m_level);
43 }
44 ~LevelIncrementer()
45 {
46 --(*m_level);
47 }
48
49private:
50 int *m_level;
51};
52
54QString elementToComponent(Element *element)
55{
56 QString customType = element->property("com.kdab.KDSME.DSMExporter.customType").toString();
57 if (!customType.isEmpty())
58 return customType;
59
60 const Element::Type type = element->type();
61 switch (type) {
63 return QStringLiteral("StateMachine");
65 return QStringLiteral("FinalState");
67 return QStringLiteral("HistoryState");
69 return QStringLiteral("State");
70 case Element::TransitionType: // fall-through
72 return QStringLiteral("SignalTransition");
74 return QStringLiteral("TimeoutTransition");
75 case Element::PseudoStateType: // fall-through
77 return QString();
78 }
79
80 Q_UNREACHABLE();
81 return QString();
82}
83
85QString toQmlId(const QString &input)
86{
87 if (input.isEmpty())
88 return input;
89
90 QString out = input;
91 std::replace_if(
92 out.begin(), out.end(),
93 [](QChar c) -> bool {
94 return !(c.isLetterOrNumber() || c == u'_');
95 },
96 u'_');
97
98 out[0] = out.at(0).toLower();
99 return out;
100}
101
102}
103
104struct QmlExporter::Private
105{
106 explicit Private(QByteArray *array);
107 explicit Private(QIODevice *device);
108
109 bool writeStateMachine(StateMachine *machine);
110 bool writeState(State *state);
111 bool writeStateInner(State *state);
112 bool writeTransition(Transition *transition);
113 void writeAttribute(Element *element, const QString &name, const QString &value);
114
115 [[nodiscard]] QString indention() const;
116
117 QTextStream m_out;
118 int m_indent, m_level;
119};
120
121QmlExporter::Private::Private(QByteArray *array)
122 : m_out(array)
123 , m_indent(4)
124 , m_level(0)
125{
126}
127
128QmlExporter::Private::Private(QIODevice *device)
129 : m_out(device)
130 , m_indent(4)
131 , m_level(0)
132{
133}
134
135QmlExporter::QmlExporter(QByteArray *array)
136 : d(new Private(array))
137{
138 Q_ASSERT(array);
139}
140
141QmlExporter::QmlExporter(QIODevice *device)
142 : d(new Private(device))
143{
144 Q_ASSERT(device);
145}
146
150
152{
153 return d->m_indent;
154}
155
157{
158 d->m_indent = indent;
159}
160
162{
163 setErrorString(QString());
164 d->m_level = 0;
165
166 if (!machine) {
167 setErrorString(QStringLiteral("Null machine instance passed"));
168 return false;
169 }
170
171 if (d->m_out.status() != QTextStream::Ok) {
172 setErrorString(QStringLiteral("Invalid QTextStream status: %1").arg(d->m_out.status()));
173 return false;
174 }
175
176 const bool success = d->writeStateMachine(machine);
177 d->m_out.flush();
178 return success;
179}
180
181bool QmlExporter::Private::writeStateMachine(StateMachine *machine)
182{
183 Q_ASSERT(machine);
184
185 const QString importStmt = QStringLiteral("import %1 %2\n").arg(QStringLiteral(KDSME_QML_MODULE)).arg(QStringLiteral(KDSME_QML_MODULE_VERSION)); // clazy:exclude=qstring-arg
186 m_out << indention() << importStmt;
187
188 const QStringList customImports = machine->property("com.kdab.KDSME.DSMExporter.customImports").toStringList();
189 for (const QString &customImport : customImports)
190 m_out << customImport << '\n';
191 m_out << '\n';
192
193 const QString qmlComponent = elementToComponent(machine);
194 m_out << indention() << QStringLiteral("%1 {\n").arg(qmlComponent);
195 if (!writeStateInner(machine))
196 return false;
197 m_out << indention() << QStringLiteral("}\n");
198 return true;
199}
200
201bool QmlExporter::Private::writeState(State *state)
202{
203 Q_ASSERT(state);
204
205 if (qobject_cast<PseudoState *>(state))
206 return true; // pseudo states are ignored
207
208 if (!state->property("com.kdab.KDSME.DSMExporter.externalSource").isNull())
209 return true; // external defined states, like sourced from other qml files, are not exported
210
211 const QString qmlComponent = elementToComponent(state);
212 m_out << indention() << QStringLiteral("%1 {\n").arg(qmlComponent);
213 if (!writeStateInner(state))
214 return false;
215 m_out << indention() << QStringLiteral("}\n");
216 return true;
217}
218
219void QmlExporter::Private::writeAttribute(Element *element, const QString &name, const QString &value)
220{
221 if (value.isEmpty())
222 return;
223
224 const QVariant implBindingNames = element->property("com.kdab.KDSME.DSMExporter.implBindingNames");
225 if (!implBindingNames.isNull()) {
226 const QString v = implBindingNames.toMap().value(name).toString();
227 if (value == v) {
228 return;
229 }
230 }
231
232 m_out << indention() << QStringLiteral("%1: %2\n").arg(name).arg(value); // clazy:exclude=qstring-arg
233}
234
235bool QmlExporter::Private::writeStateInner(State *state)
236{
237 Q_ASSERT(state);
238
239 const LevelIncrementer levelinc(&m_level);
240 Q_UNUSED(levelinc);
241
242 writeAttribute(state, QStringLiteral("id"), toQmlId(state->label()));
243
244 if (auto *stateMachine = qobject_cast<StateMachine *>(state)) {
245 const QString running = stateMachine->property("com.kdab.KDSME.DSMExporter.running").toString();
246 writeAttribute(state, QStringLiteral("running"), running);
247 }
248
249 if (state->childMode() == State::ParallelStates) {
250 writeAttribute(state, QStringLiteral("childMode"), QStringLiteral("State.ParallelStates"));
251 }
252
253 if (const State *initial = ElementUtil::findInitialState(state)) {
254 writeAttribute(state, QStringLiteral("initialState"), toQmlId(initial->label()));
255 }
256
257 if (auto *historyState = qobject_cast<HistoryState *>(state)) {
258 if (const State *defaultState = historyState->defaultState())
259 writeAttribute(state, QStringLiteral("defaultState"), toQmlId(defaultState->label()));
260 if (historyState->historyType() == HistoryState::DeepHistory)
261 writeAttribute(state, QStringLiteral("historyType"), QStringLiteral("HistoryState.DeepHistory"));
262 }
263
264 writeAttribute(state, QStringLiteral("onEntered"), state->onEntry());
265 writeAttribute(state, QStringLiteral("onExited"), state->onExit());
266
267 const auto childStates = state->childStates();
268 if (std::any_of(childStates.begin(), childStates.end(),
269 [this](State *child) { return !writeState(child); })) {
270 return false;
271 }
272
273 const auto stateTransitions = state->transitions();
274 return std::all_of(stateTransitions.begin(), stateTransitions.end(),
275 [this](Transition *child) { return writeTransition(child); });
276}
277
278bool QmlExporter::Private::writeTransition(Transition *transition)
279{
280 Q_ASSERT(transition);
281 m_out << indention() << QStringLiteral("%1 {\n").arg(elementToComponent(transition));
282 {
283 const LevelIncrementer levelinc(&m_level);
284 Q_UNUSED(levelinc);
285
286 writeAttribute(transition, QStringLiteral("id"), toQmlId(transition->label()));
287
288 if (transition->targetState()) {
289 writeAttribute(transition, QStringLiteral("targetState"), toQmlId(transition->targetState()->label()));
290 }
291
292 if (transition->type() == Element::SignalTransitionType) {
293 auto t = qobject_cast<SignalTransition *>(transition);
294 writeAttribute(transition, QStringLiteral("signal"), t->signal());
295 }
296
297 if (transition->type() == Element::TimeoutTransitionType) {
298 auto t = qobject_cast<TimeoutTransition *>(transition);
299 if (t->timeout() != -1) {
300 writeAttribute(transition, QStringLiteral("timeout"), QString::number(t->timeout()));
301 }
302 }
303
304 writeAttribute(transition, QStringLiteral("guard"), transition->guard());
305 }
306 m_out << indention() << QStringLiteral("}\n");
307 return true;
308}
309
310QString QmlExporter::Private::indention() const
311{
312 return QString { m_indent * m_level, u' ' };
313}
void setErrorString(const QString &errorString)
QString label
Definition element.h:40
@ TimeoutTransitionType
Definition element.h:58
@ SignalTransitionType
Definition element.h:57
void setIndent(int indent)
virtual bool exportMachine(StateMachine *machine) override
QmlExporter(QByteArray *array)
ChildMode childMode
Definition state.h:33
QList< Transition * > transitions() const
Definition state.cpp:98
QList< State * > childStates() const
Definition state.cpp:93
@ ParallelStates
Definition state.h:41
QString onExit
Definition state.h:32
QString onEntry
Definition state.h:31
Type type() const override
KDSME::State * targetState
Definition transition.h:27
KDSME_CORE_EXPORT State * findInitialState(const State *state)
#define KDSME_QML_MODULE
#define KDSME_QML_MODULE_VERSION

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDStateMachineEditor
Create Qt State Machine metacode using a graphical user interface
https://github.com/KDAB/KDStateMachineEditor
Generated on Tue Jul 15 2025 15:21:47 for KDStateMachineEditor API Documentation by doxygen 1.9.8