24#include <QXmlStreamReader>
30struct ScxmlImporter::Private
43 void visitTransiton(
State *parent);
44 void visitState(
State *parent);
45 void visitInitial(
State *parent);
46 void visitParallel(
State *parent);
47 void visitFinal(
State *parent);
48 void visitHistory(
State *parent);
56 void initState(
State *state);
59 Transition *createTransition(
State *parent,
const QString &targetStateId);
61 void raiseUnexpectedElementError(
const QString &context);
64 void resolveTargetStates();
68 QXmlStreamReader m_reader;
71 QHash<Transition *, QString> m_unresolvedTargetStateIds;
72 QHash<QString, State *> m_nameToStateMap;
78 : d(new Private(this))
92 if (d->m_data.isEmpty()) {
97 d->m_reader.addData(d->m_data);
101 if (d->m_reader.readNextStartElement() && d->m_reader.name() == QStringLiteral(
"scxml")) {
102 stateMachine = d->visitScxml();
104 d->m_reader.raiseError(tr(
"This document does not start with an <scxml> element"));
107 if (!d->m_reader.hasError()) {
110 d->resolveTargetStates();
113 if (d->m_reader.hasError()) {
118 stateMachine =
nullptr;
125 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"scxml"));
126 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
128 const QXmlStreamAttributes attributes = m_reader.attributes();
131 state->
setLabel(attributes.value(QStringLiteral(
"name")).toString());
133 tryCreateInitialState(state);
135 while (m_reader.readNextStartElement()) {
136 if (m_reader.name() == QStringLiteral(
"state")) {
138 }
else if (m_reader.name() == QStringLiteral(
"parallel")) {
139 visitParallel(state);
140 }
else if (m_reader.name() == QStringLiteral(
"final")) {
142 }
else if (m_reader.name() == QStringLiteral(
"datamodel")) {
143 m_reader.skipCurrentElement();
144 }
else if (m_reader.name() == QStringLiteral(
"script")) {
145 m_reader.skipCurrentElement();
147 raiseUnexpectedElementError(QStringLiteral(
"scxml"));
153void ScxmlImporter::Private::visitParallel(
State *parent)
155 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"parallel"));
156 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
158 auto *state =
new State(parent);
161 tryCreateInitialState(state);
163 while (m_reader.readNextStartElement()) {
164 if (m_reader.name() == QStringLiteral(
"onentry") || m_reader.name() == QStringLiteral(
"onexit")) {
165 m_reader.skipCurrentElement();
166 }
else if (m_reader.name() == QStringLiteral(
"transition")) {
167 visitTransiton(state);
168 }
else if (m_reader.name() == QStringLiteral(
"state")) {
170 }
else if (m_reader.name() == QStringLiteral(
"parallel")) {
171 visitParallel(state);
172 }
else if (m_reader.name() == QStringLiteral(
"datamodel")) {
173 m_reader.skipCurrentElement();
174 }
else if (m_reader.name() == QStringLiteral(
"invoke")) {
175 m_reader.skipCurrentElement();
176 }
else if (m_reader.name() == QStringLiteral(
"history")) {
179 raiseUnexpectedElementError(QStringLiteral(
"parallel"));
184void ScxmlImporter::Private::visitState(
State *parent)
186 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"state"));
187 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
189 auto state =
new State(parent);
191 tryCreateInitialState(state);
193 while (m_reader.readNextStartElement()) {
194 if (m_reader.name() == QStringLiteral(
"onentry") || m_reader.name() == QStringLiteral(
"onexit")) {
195 m_reader.skipCurrentElement();
196 }
else if (m_reader.name() == QStringLiteral(
"transition")) {
197 visitTransiton(state);
198 }
else if (m_reader.name() == QStringLiteral(
"initial")) {
200 }
else if (m_reader.name() == QStringLiteral(
"state")) {
202 }
else if (m_reader.name() == QStringLiteral(
"parallel")) {
203 visitParallel(state);
204 }
else if (m_reader.name() == QStringLiteral(
"final")) {
206 }
else if (m_reader.name() == QStringLiteral(
"history")) {
208 }
else if (m_reader.name() == QStringLiteral(
"datamodel")) {
209 m_reader.skipCurrentElement();
210 }
else if (m_reader.name() == QStringLiteral(
"invoke")) {
211 m_reader.skipCurrentElement();
213 raiseUnexpectedElementError(QStringLiteral(
"state"));
218void ScxmlImporter::Private::visitInitial(
State *parent)
220 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"initial"));
221 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
225 while (m_reader.readNextStartElement()) {
226 if (m_reader.name() == u
"transition") {
228 const QXmlStreamAttributes attributes = m_reader.attributes();
229 const QString targetStateId = attributes.value(QStringLiteral(
"target")).toString();
230 transition = createTransition(initialState, targetStateId);
232 raiseUnexpectedElementError(QStringLiteral(
"initial"));
236 m_reader.raiseError(QStringLiteral(
"Encountered <initial> element with invalid <transition> child"));
239 m_reader.skipCurrentElement();
242void ScxmlImporter::Private::visitFinal(
State *parent)
244 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"final"));
245 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
250 m_reader.skipCurrentElement();
253void ScxmlImporter::Private::visitTransiton(
State *parent)
255 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"transition"));
256 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
258 const QXmlStreamAttributes attributes = m_reader.attributes();
259 const QString
event = attributes.value(QLatin1String(
"event")).toString();
260 const QString targetStateId = attributes.value(QLatin1String(
"target")).toString();
261 Transition *transition = createTransition(parent, targetStateId);
266 m_reader.skipCurrentElement();
269void ScxmlImporter::Private::visitHistory(
State *parent)
272 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == u
"transition");
273 IF_DEBUG(qCDebug(KDSME_CORE) << Q_FUNC_INFO;)
275 qCWarning(KDSME_CORE) <<
"NYI";
277 m_reader.skipCurrentElement();
280void ScxmlImporter::Private::resolveTargetStates()
282 IF_DEBUG(qCDebug(KDSME_CORE) << m_nameToStateMap;)
284 auto it = m_unresolvedTargetStateIds.constBegin();
285 while (it != m_unresolvedTargetStateIds.constEnd()) {
286 const QString targetStateId = it.value();
287 State *targetState = m_nameToStateMap.value(targetStateId);
289 m_reader.raiseError(QStringLiteral(
"Unknown state id: %1").arg(targetStateId));
299State *ScxmlImporter::Private::tryCreateInitialState(
State *parent)
301 const QXmlStreamAttributes attributes = m_reader.attributes();
302 if (attributes.hasAttribute(QStringLiteral(
"initial"))) {
304 const QString initialStateId = attributes.value(QStringLiteral(
"initial")).toString();
305 createTransition(initialState, initialStateId);
310void ScxmlImporter::Private::initState(
State *state)
314 const QXmlStreamAttributes attributes = m_reader.attributes();
315 const QString
id = attributes.value(QStringLiteral(
"id")).toString();
318 qCWarning(KDSME_CORE) <<
"Unnamed state at offset:" << m_reader.characterOffset();
321 m_nameToStateMap[id] = state;
324Transition *ScxmlImporter::Private::createTransition(
State *parent,
const QString &targetStateId)
326 IF_DEBUG(qCDebug(KDSME_CORE) << parent->
label() << targetStateId);
327 if (targetStateId.isEmpty()) {
332 m_unresolvedTargetStateIds[transition] = targetStateId;
336void ScxmlImporter::Private::reset()
338 m_nameToStateMap.clear();
339 m_unresolvedTargetStateIds.clear();
343void ScxmlImporter::Private::raiseUnexpectedElementError(
const QString &context)
345 m_reader.raiseError(QStringLiteral(
"Unexpected element found while parsing '%1': %2")
346 .arg(context, m_reader.name().toString()));
void setErrorString(const QString &errorString)
void setLabel(const QString &label)
ScxmlImporter(const QByteArray &data)
StateMachine * import() override
void setTargetState(State *targetState)