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 "qscxmlnulldatamodel.h" |
5 | #include "qscxmlevent.h" |
6 | #include "qscxmlstatemachine_p.h" |
7 | #include "qscxmltabledata.h" |
8 | #include "qscxmldatamodel_p.h" |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | class QScxmlNullDataModelPrivate : public QScxmlDataModelPrivate |
13 | { |
14 | Q_DECLARE_PUBLIC(QScxmlNullDataModel) |
15 | |
16 | struct ResolvedEvaluatorInfo { |
17 | bool error; |
18 | QString str; |
19 | |
20 | ResolvedEvaluatorInfo() |
21 | : error(false) |
22 | {} |
23 | }; |
24 | |
25 | public: |
26 | bool evalBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
27 | { |
28 | Q_Q(QScxmlNullDataModel); |
29 | Q_ASSERT(ok); |
30 | |
31 | ResolvedEvaluatorInfo info; |
32 | Resolved::const_iterator it = resolved.find(key: id); |
33 | if (it == resolved.end()) { |
34 | info = prepare(id); |
35 | } else { |
36 | info = it.value(); |
37 | } |
38 | |
39 | if (info.error) { |
40 | *ok = false; |
41 | QScxmlStateMachinePrivate::get(t: q->stateMachine())->submitError(QStringLiteral("error.execution" ), msg: info.str); |
42 | return false; |
43 | } |
44 | |
45 | *ok = true; |
46 | return q->stateMachine()->isActive(scxmlStateName: info.str); |
47 | } |
48 | |
49 | ResolvedEvaluatorInfo prepare(QScxmlExecutableContent::EvaluatorId id) |
50 | { |
51 | auto td = m_stateMachine->tableData(); |
52 | const QScxmlExecutableContent::EvaluatorInfo &info = td->evaluatorInfo(evaluatorId: id); |
53 | QString expr = td->string(id: info.expr); |
54 | for (int i = 0; i < expr.size(); ) { |
55 | QChar ch = expr.at(i); |
56 | if (ch.isSpace()) { |
57 | expr.remove(i, len: 1); |
58 | } else { |
59 | ++i; |
60 | } |
61 | } |
62 | |
63 | ResolvedEvaluatorInfo resolved; |
64 | if (expr.startsWith(QStringLiteral("In(" )) && expr.endsWith(c: QLatin1Char(')'))) { |
65 | resolved.error = false; |
66 | resolved.str = expr.mid(position: 3, n: expr.size() - 4); |
67 | } else { |
68 | resolved.error = true; |
69 | resolved.str = QStringLiteral("%1 in %2" ).arg(args&: expr, args: td->string(id: info.context)); |
70 | } |
71 | return resolved; |
72 | } |
73 | |
74 | private: |
75 | typedef QHash<QScxmlExecutableContent::EvaluatorId, ResolvedEvaluatorInfo> Resolved; |
76 | Resolved resolved; |
77 | }; |
78 | |
79 | /*! |
80 | * \class QScxmlNullDataModel |
81 | * \brief The QScxmlNullDataModel class is the null data model for a Qt SCXML |
82 | * stateMachine. |
83 | * \since 5.7 |
84 | * \inmodule QtScxml |
85 | * |
86 | * This class implements the null data model as described in the |
87 | * \l {SCXML Specification - B.1 The Null Data Model}. Using the value \c "null" |
88 | * for the \e datamodel attribute of the \c <scxml> element means that there is |
89 | * no underlying data model, but some executable content, like \c In(...) or |
90 | * \c <log> can still be used. |
91 | * |
92 | * \sa QScxmlStateMachine QScxmlDataModel |
93 | */ |
94 | |
95 | /*! |
96 | * Creates a new Qt SCXML null data model, with the parent object \a parent. |
97 | */ |
98 | QScxmlNullDataModel::QScxmlNullDataModel(QObject *parent) |
99 | : QScxmlDataModel(*(new QScxmlNullDataModelPrivate), parent) |
100 | {} |
101 | |
102 | /*! |
103 | Destroys the data model. |
104 | */ |
105 | QScxmlNullDataModel::~QScxmlNullDataModel() |
106 | { |
107 | } |
108 | |
109 | /*! |
110 | \reimp |
111 | */ |
112 | bool QScxmlNullDataModel::setup(const QVariantMap &initialDataValues) |
113 | { |
114 | Q_UNUSED(initialDataValues); |
115 | |
116 | return true; |
117 | } |
118 | |
119 | /*! |
120 | \reimp |
121 | Evaluates the executable content pointed to by \a id and records in \a ok |
122 | whether there was an error. Returns the result of the evaluation as a string. |
123 | The null data model can evaluate the \c <log> element, so this might result in |
124 | an actual value, rather than an error |
125 | */ |
126 | QString QScxmlNullDataModel::evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
127 | { |
128 | Q_D(QScxmlNullDataModel); |
129 | // We do implement this, because <log> is allowed in the Null data model, |
130 | // and <log> has an expr attribute that needs "evaluation" for it to generate the log message. |
131 | *ok = true; |
132 | auto td = d->m_stateMachine->tableData(); |
133 | const QScxmlExecutableContent::EvaluatorInfo &info = td->evaluatorInfo(evaluatorId: id); |
134 | return td->string(id: info.expr); |
135 | } |
136 | |
137 | /*! |
138 | \reimp |
139 | Evaluates the executable content pointed to by \a id and records in \a ok |
140 | whether there was an error. Returns the result of the evaluation as a boolean |
141 | value. The null data model can evaluate the instruction \c In(...), so this |
142 | might result in an actual value, rather than an error. |
143 | */ |
144 | bool QScxmlNullDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
145 | { |
146 | Q_D(QScxmlNullDataModel); |
147 | return d->evalBool(id, ok); |
148 | } |
149 | |
150 | /*! |
151 | \reimp |
152 | Evaluates the executable content pointed to by \a id and records in \a ok |
153 | whether there was an error. As this is the null data model, any evaluation will in |
154 | fact result in an error, with \a ok set to \c false. Returns an empty QVariant. |
155 | */ |
156 | QVariant QScxmlNullDataModel::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
157 | { |
158 | Q_UNUSED(id); |
159 | *ok = false; |
160 | QScxmlStateMachinePrivate::get(t: stateMachine())->submitError( |
161 | QStringLiteral("error.execution" ), |
162 | QStringLiteral("Cannot evaluate expressions on a null data model" )); |
163 | return QVariant(); |
164 | } |
165 | |
166 | /*! |
167 | \reimp |
168 | Evaluates the executable content pointed to by \a id and records in \a ok |
169 | whether there was an error. As this is the null data model, any evaluation will in |
170 | fact result in an error, with \a ok set to \c false. |
171 | */ |
172 | void QScxmlNullDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
173 | { |
174 | Q_UNUSED(id); |
175 | *ok = false; |
176 | QScxmlStateMachinePrivate::get(t: stateMachine())->submitError( |
177 | QStringLiteral("error.execution" ), |
178 | QStringLiteral("Cannot evaluate expressions on a null data model" )); |
179 | } |
180 | |
181 | /*! |
182 | \reimp |
183 | Throws an error and sets \a ok to \c false, because the null data model cannot evaluate |
184 | assignments. |
185 | */ |
186 | void QScxmlNullDataModel::evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
187 | { |
188 | Q_UNUSED(id); |
189 | *ok = false; |
190 | QScxmlStateMachinePrivate::get(t: stateMachine())->submitError( |
191 | QStringLiteral("error.execution" ), |
192 | QStringLiteral("Cannot assign values on a null data model" )); |
193 | } |
194 | |
195 | /*! |
196 | \reimp |
197 | Throws an error and sets \a ok to \c false, because the null data model cannot |
198 | initialize data. |
199 | */ |
200 | void QScxmlNullDataModel::evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) |
201 | { |
202 | Q_UNUSED(id); |
203 | *ok = false; |
204 | QScxmlStateMachinePrivate::get(t: stateMachine())->submitError( |
205 | QStringLiteral("error.execution" ), |
206 | QStringLiteral("Cannot initialize values on a null data model" )); |
207 | } |
208 | |
209 | /*! |
210 | \reimp |
211 | Throws an error and sets \a ok to \c false, because the null data model cannot |
212 | evaluate \c <foreach> blocks. |
213 | */ |
214 | void QScxmlNullDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, |
215 | ForeachLoopBody *body) |
216 | { |
217 | Q_UNUSED(id); |
218 | Q_UNUSED(body); |
219 | *ok = false; |
220 | QScxmlStateMachinePrivate::get(t: stateMachine())->submitError( |
221 | QStringLiteral("error.execution" ), |
222 | QStringLiteral("Cannot run foreach on a null data model" )); |
223 | } |
224 | |
225 | /*! |
226 | * \reimp |
227 | * Does not actually set the \a event, because the null data model does not handle events. |
228 | */ |
229 | void QScxmlNullDataModel::setScxmlEvent(const QScxmlEvent &event) |
230 | { |
231 | Q_UNUSED(event); |
232 | } |
233 | |
234 | /*! |
235 | * \reimp |
236 | * Returns an invalid variant, because the null data model does not support |
237 | * properties. |
238 | */ |
239 | QVariant QScxmlNullDataModel::scxmlProperty(const QString &name) const |
240 | { |
241 | Q_UNUSED(name); |
242 | return QVariant(); |
243 | } |
244 | |
245 | /*! |
246 | * \reimp |
247 | * Returns \c false, because the null data model does not support properties. |
248 | */ |
249 | bool QScxmlNullDataModel::hasScxmlProperty(const QString &name) const |
250 | { |
251 | Q_UNUSED(name); |
252 | return false; |
253 | } |
254 | |
255 | /*! |
256 | * \reimp |
257 | * Returns \c false, because the null data model does not support properties. |
258 | */ |
259 | bool QScxmlNullDataModel::setScxmlProperty(const QString &name, const QVariant &value, const QString &context) |
260 | { |
261 | Q_UNUSED(name); |
262 | Q_UNUSED(value); |
263 | Q_UNUSED(context); |
264 | return false; |
265 | } |
266 | |
267 | QT_END_NAMESPACE |
268 | |