1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtScxml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QSCXMLSTATEMACHINE_P_H
41#define QSCXMLSTATEMACHINE_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtScxml/private/qscxmlexecutablecontent_p.h>
55#include <QtScxml/qscxmlstatemachine.h>
56#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
57#include <QtCore/private/qobject_p.h>
58#include <QtCore/private/qmetaobject_p.h>
59#include <QtCore/qmetaobject.h>
60
61QT_BEGIN_NAMESPACE
62
63namespace QScxmlInternal {
64class EventLoopHook: public QObject
65{
66 Q_OBJECT
67
68 QScxmlStateMachinePrivate *smp;
69
70public:
71 EventLoopHook(QScxmlStateMachinePrivate *smp)
72 : smp(smp)
73 {}
74
75 void queueProcessEvents();
76
77 Q_INVOKABLE void doProcessEvents();
78
79protected:
80 void timerEvent(QTimerEvent *timerEvent) override;
81};
82
83class ScxmlEventRouter : public QObject
84{
85 Q_OBJECT
86public:
87 ScxmlEventRouter(QObject *parent = nullptr) : QObject(parent) {}
88 QMetaObject::Connection connectToEvent(const QStringList &segments, const QObject *receiver,
89 const char *method, Qt::ConnectionType type);
90 QMetaObject::Connection connectToEvent(const QStringList &segments, const QObject *receiver,
91 void **slot, QtPrivate::QSlotObjectBase *method,
92 Qt::ConnectionType type);
93
94 void route(const QStringList &segments, QScxmlEvent *event);
95
96signals:
97 void eventOccurred(const QScxmlEvent &event);
98
99private:
100 QHash<QString, ScxmlEventRouter *> children;
101 ScxmlEventRouter *child(const QString &segment);
102
103 void disconnectNotify(const QMetaMethod &signal) override;
104};
105
106class StateMachineInfoProxy: public QObject
107{
108 Q_OBJECT
109
110public:
111 StateMachineInfoProxy(QObject *parent)
112 : QObject(parent)
113 {}
114
115Q_SIGNALS:
116 void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
117 void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
118 void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
119};
120} // QScxmlInternal namespace
121
122class QScxmlInvokableService;
123class QScxmlStateMachinePrivate: public QObjectPrivate
124{
125 Q_DECLARE_PUBLIC(QScxmlStateMachine)
126
127 static QAtomicInt m_sessionIdCounter;
128
129public: // types
130 typedef QScxmlExecutableContent::StateTable StateTable;
131
132 class HistoryContent
133 {
134 QHash<int, int> storage;
135
136 public:
137 HistoryContent() { storage.reserve(asize: 4); }
138
139 int &operator[](int idx) {
140 QHash<int, int>::Iterator i = storage.find(akey: idx);
141 return (i == storage.end()) ? storage.insert(akey: idx, avalue: StateTable::InvalidIndex).value() :
142 i.value();
143 }
144
145 int value(int idx) const {
146 QHash<int, int>::ConstIterator i = storage.constFind(akey: idx);
147 return (i == storage.constEnd()) ? StateTable::InvalidIndex : i.value();
148 }
149 };
150
151 class ParserData
152 {
153 public:
154 QScopedPointer<QScxmlDataModel> m_ownedDataModel;
155 QVector<QScxmlError> m_errors;
156 };
157
158 // The OrderedSet is a set where it elements are in insertion order. See
159 // http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation under Algorithm, Datatypes. It
160 // is used to keep lists of states and transitions in document order.
161 class OrderedSet
162 {
163 std::vector<int> storage;
164
165 public:
166 OrderedSet(){}
167 OrderedSet(std::initializer_list<int> l): storage(l) {}
168
169 std::vector<int> takeList() const
170 { return std::move(storage); }
171
172 const std::vector<int> &list() const
173 { return storage; }
174
175 bool contains(int i) const
176 {
177 return std::find(first: storage.cbegin(), last: storage.cend(), val: i) != storage.cend();
178 }
179
180 bool remove(int i)
181 {
182 std::vector<int>::iterator it = std::find(first: storage.begin(), last: storage.end(), val: i);
183 if (it == storage.end()) {
184 return false;
185 }
186 storage.erase(position: it);
187 return true;
188 }
189
190 void removeHead()
191 { if (!isEmpty()) storage.erase(position: storage.begin()); }
192
193 bool isEmpty() const
194 { return storage.empty(); }
195
196 void add(int i)
197 { if (!contains(i)) storage.push_back(x: i); }
198
199 bool intersectsWith(const OrderedSet &other) const
200 {
201 for (auto i : storage) {
202 if (other.contains(i)) {
203 return true;
204 }
205 }
206 return false;
207 }
208
209 void clear()
210 { storage.clear(); }
211
212 typedef std::vector<int>::const_iterator const_iterator;
213 const_iterator begin() const { return storage.cbegin(); }
214 const_iterator end() const { return storage.cend(); }
215 };
216
217 class Queue
218 {
219 QVector<QScxmlEvent *> storage;
220
221 public:
222 Queue()
223 { storage.reserve(asize: 4); }
224
225 ~Queue()
226 { qDeleteAll(c: storage); }
227
228 void enqueue(QScxmlEvent *e)
229 { storage.append(t: e); }
230
231 bool isEmpty() const
232 { return storage.empty(); }
233
234 QScxmlEvent *dequeue()
235 {
236 Q_ASSERT(!isEmpty());
237 QScxmlEvent *e = storage.first();
238 storage.pop_front();
239 int sz = storage.size();
240 if (Q_UNLIKELY(sz > 4 && sz * 8 < storage.capacity())) {
241 storage.squeeze();
242 }
243 return e;
244 }
245 };
246
247public:
248 QScxmlStateMachinePrivate(const QMetaObject *qMetaObject);
249 ~QScxmlStateMachinePrivate();
250
251 static QScxmlStateMachinePrivate *get(QScxmlStateMachine *t)
252 { return t->d_func(); }
253
254 static QString generateSessionId(const QString &prefix);
255
256 ParserData *parserData();
257
258 void setIsInvoked(bool invoked)
259 { m_isInvoked = invoked; }
260
261 void addService(int invokingState);
262 void removeService(int invokingState);
263 QScxmlInvokableServiceFactory *serviceFactory(int id);
264
265 bool executeInitialSetup();
266
267 void routeEvent(QScxmlEvent *event);
268 void postEvent(QScxmlEvent *event);
269 void submitDelayedEvent(QScxmlEvent *event);
270 void submitError(const QString &type, const QString &msg, const QString &sendid = QString());
271
272 void start();
273 void pause();
274 void processEvents();
275
276 void setEvent(QScxmlEvent *event);
277 void resetEvent();
278
279 void emitStateActive(int stateIndex, bool active);
280 void emitInvokedServicesChanged();
281
282 void attach(QScxmlStateMachineInfo *info);
283 const OrderedSet &configuration() const { return m_configuration; }
284
285 void updateMetaCache();
286
287private:
288 QStringList stateNames(const std::vector<int> &stateIndexes) const;
289 std::vector<int> historyStates(int stateIdx) const;
290
291 void exitInterpreter();
292 void returnDoneEvent(QScxmlExecutableContent::ContainerId doneData);
293 bool nameMatch(const StateTable::Array &patterns, QScxmlEvent *event) const;
294 void selectTransitions(OrderedSet &enabledTransitions,
295 const std::vector<int> &configInDocumentOrder,
296 QScxmlEvent *event) const;
297 void removeConflictingTransitions(OrderedSet *enabledTransitions) const;
298 void getProperAncestors(std::vector<int> *ancestors, int state1, int state2) const;
299 void microstep(const OrderedSet &enabledTransitions);
300 void exitStates(const OrderedSet &enabledTransitions);
301 void computeExitSet(const OrderedSet &enabledTransitions, OrderedSet &statesToExit) const;
302 void executeTransitionContent(const OrderedSet &enabledTransitions);
303 void enterStates(const OrderedSet &enabledTransitions);
304 void computeEntrySet(const OrderedSet &enabledTransitions,
305 OrderedSet *statesToEnter,
306 OrderedSet *statesForDefaultEntry,
307 HistoryContent *defaultHistoryContent) const;
308 void addDescendantStatesToEnter(int stateIndex,
309 OrderedSet *statesToEnter,
310 OrderedSet *statesForDefaultEntry,
311 HistoryContent *defaultHistoryContent) const;
312 void addAncestorStatesToEnter(int stateIndex,
313 int ancestorIndex,
314 OrderedSet *statesToEnter,
315 OrderedSet *statesForDefaultEntry,
316 HistoryContent *defaultHistoryContent) const;
317 std::vector<int> getChildStates(const StateTable::State &state) const;
318 bool hasDescendant(const OrderedSet &statesToEnter, int childIdx) const;
319 bool allDescendants(const OrderedSet &statesToEnter, int childdx) const;
320 bool isDescendant(int state1, int state2) const;
321 bool allInFinalStates(const std::vector<int> &states) const;
322 bool someInFinalStates(const std::vector<int> &states) const;
323 bool isInFinalState(int stateIndex) const;
324 int getTransitionDomain(int transitionIndex) const;
325 int findLCCA(OrderedSet &&states) const;
326 void getEffectiveTargetStates(OrderedSet *targets, int transitionIndex) const;
327
328public: // types & data fields:
329 QString m_sessionId;
330 bool m_isInvoked;
331 bool m_isInitialized;
332 bool m_isProcessingEvents;
333 QVariantMap m_initialValues;
334 QScxmlDataModel *m_dataModel;
335 QScxmlCompilerPrivate::DefaultLoader m_defaultLoader;
336 QScxmlCompiler::Loader *m_loader;
337 QScxmlExecutionEngine *m_executionEngine;
338 QScxmlTableData *m_tableData;
339 const StateTable *m_stateTable;
340 QScxmlStateMachine *m_parentStateMachine;
341 QScxmlInternal::EventLoopHook m_eventLoopHook;
342 typedef std::vector<std::pair<int, QScxmlEvent *>> DelayedQueue;
343 DelayedQueue m_delayedEvents;
344 const QMetaObject *m_metaObject;
345 QScxmlInternal::ScxmlEventRouter m_router;
346
347private:
348 QScopedPointer<ParserData> m_parserData; // used when created by StateMachine::fromFile.
349 typedef QHash<int, QVector<int>> HistoryValues;
350 struct InvokedService {
351 int invokingState;
352 QScxmlInvokableService *service;
353 QString serviceName;
354 };
355
356 // TODO: move the stuff below to a struct that can be reset
357 HistoryValues m_historyValue;
358 OrderedSet m_configuration;
359 Queue m_internalQueue;
360 Queue m_externalQueue;
361 QSet<int> m_statesToInvoke;
362 std::vector<InvokedService> m_invokedServices;
363 std::vector<bool> m_isFirstStateEntry;
364 std::vector<QScxmlInvokableServiceFactory *> m_cachedFactories;
365 enum { Invalid = 0, Starting, Running, Paused, Finished } m_runningState = Invalid;
366 bool isRunnable() const {
367 switch (m_runningState) {
368 case Starting:
369 case Running:
370 case Paused:
371 return true;
372 case Invalid:
373 case Finished:
374 return false;
375 }
376
377 return false; // Dead code, but many dumb compilers cannot (or are unwilling to) detect that.
378 }
379
380 bool isPaused() const { return m_runningState == Paused; }
381
382 QScxmlInternal::StateMachineInfoProxy *m_infoSignalProxy;
383
384 QHash<int, int> m_stateIndexToSignalIndex;
385 QHash<QString, int> m_stateNameToSignalIndex;
386};
387
388QT_END_NAMESPACE
389
390#endif // QSCXMLSTATEMACHINE_P_H
391
392

source code of qtscxml/src/scxml/qscxmlstatemachine_p.h