| 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 | #include "qscxmlexecutablecontent_p.h" | 
| 41 | #include "qscxmlevent_p.h" | 
| 42 | #include "qscxmlstatemachine_p.h" | 
| 43 |  | 
| 44 | #include <qjsondocument.h> | 
| 45 | #include <qjsonobject.h> | 
| 46 |  | 
| 47 | QT_BEGIN_NAMESPACE | 
| 48 |  | 
| 49 | using namespace QScxmlExecutableContent; | 
| 50 |  | 
| 51 | QAtomicInt QScxmlEventBuilder::idCounter = QAtomicInt(0); | 
| 52 |  | 
| 53 | QScxmlEvent *QScxmlEventBuilder::buildEvent() | 
| 54 | { | 
| 55 |     auto dataModel = stateMachine ? stateMachine->dataModel() : nullptr; | 
| 56 |     auto tableData = stateMachine ? stateMachine->tableData() : nullptr; | 
| 57 |  | 
| 58 |     QString eventName = event; | 
| 59 |     bool ok = true; | 
| 60 |     if (eventexpr != NoEvaluator) { | 
| 61 |         eventName = dataModel->evaluateToString(id: eventexpr, ok: &ok); | 
| 62 |         ok = true; // ignore failure. | 
| 63 |     } | 
| 64 |  | 
| 65 |     QVariant data; | 
| 66 |     if ((!params || params->count == 0) && (!namelist || namelist->count == 0)) { | 
| 67 |         if (contentExpr == NoEvaluator) { | 
| 68 |             data = contents; | 
| 69 |         } else { | 
| 70 |             data = dataModel->evaluateToVariant(id: contentExpr, ok: &ok); | 
| 71 |         } | 
| 72 |         if (!ok) { | 
| 73 |             // expr evaluation failure results in the data property of the event being set to null. See e.g. test528. | 
| 74 |             data = QVariant(QMetaType::VoidStar, 0); | 
| 75 |         } | 
| 76 |     } else { | 
| 77 |         QVariantMap keyValues; | 
| 78 |         if (evaluate(params, stateMachine, keyValues)) { | 
| 79 |             if (namelist) { | 
| 80 |                 for (qint32 i = 0; i < namelist->count; ++i) { | 
| 81 |                     QString name = tableData->string(id: namelist->const_data()[i]); | 
| 82 |                     keyValues.insert(akey: name, avalue: dataModel->scxmlProperty(name)); | 
| 83 |                 } | 
| 84 |             } | 
| 85 |             data = keyValues; | 
| 86 |         } else { | 
| 87 |             // If the evaluation of the <param> tags fails, set _event.data to an empty string. | 
| 88 |             // See test343. | 
| 89 |             data = QVariant(QMetaType::VoidStar, 0); | 
| 90 |         } | 
| 91 |     } | 
| 92 |  | 
| 93 |     QString sendid = id; | 
| 94 |     if (!idLocation.isEmpty()) { | 
| 95 |         sendid = generateId(); | 
| 96 |         ok = stateMachine->dataModel()->setScxmlProperty(name: idLocation, value: sendid, context: tableData->string(id: instructionLocation)); | 
| 97 |         if (!ok) | 
| 98 |             return nullptr; | 
| 99 |     } | 
| 100 |  | 
| 101 |     QString origin = target; | 
| 102 |     if (targetexpr != NoEvaluator) { | 
| 103 |         origin = dataModel->evaluateToString(id: targetexpr, ok: &ok); | 
| 104 |         if (!ok) | 
| 105 |             return nullptr; | 
| 106 |     } | 
| 107 |     if (origin.isEmpty()) { | 
| 108 |         if (eventType == QScxmlEvent::ExternalEvent) { | 
| 109 |             origin = QStringLiteral("#_internal" ); | 
| 110 |         } | 
| 111 |     } else if (origin == QStringLiteral("#_parent" )) { | 
| 112 |         // allow sending messages to the parent, independently of whether we're invoked or not. | 
| 113 |     } else if (!origin.startsWith(c: QLatin1Char('#'))) { | 
| 114 |         // [6.2.4] and test194. | 
| 115 |         submitError(QStringLiteral("error.execution" ), | 
| 116 |                     QStringLiteral("Error in %1: %2 is not a legal target" ) | 
| 117 |                     .arg(args: tableData->string(id: instructionLocation), args&: origin), | 
| 118 |                     sendid); | 
| 119 |         return nullptr; | 
| 120 |     } else if (!stateMachine->isDispatchableTarget(target: origin)) { | 
| 121 |         // [6.2.4] and test521. | 
| 122 |         submitError(QStringLiteral("error.communication" ), | 
| 123 |                     QStringLiteral("Error in %1: cannot dispatch to target '%2'" ) | 
| 124 |                     .arg(args: tableData->string(id: instructionLocation), args&: origin), | 
| 125 |                     sendid); | 
| 126 |         return nullptr; | 
| 127 |     } | 
| 128 |  | 
| 129 |     QString origintype = type; | 
| 130 |     if (origintype.isEmpty()) { | 
| 131 |         // [6.2.5] and test198 | 
| 132 |         origintype = QStringLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor" ); | 
| 133 |     } | 
| 134 |     if (typeexpr != NoEvaluator) { | 
| 135 |         origintype = dataModel->evaluateToString(id: typeexpr, ok: &ok); | 
| 136 |         if (!ok) | 
| 137 |             return nullptr; | 
| 138 |     } | 
| 139 |     if (!origintype.isEmpty() | 
| 140 |             && origintype != QStringLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor" )) { | 
| 141 |         // [6.2.5] and test199 | 
| 142 |         submitError(QStringLiteral("error.execution" ), | 
| 143 |                     QStringLiteral("Error in %1: %2 is not a valid type" ) | 
| 144 |                     .arg(args: tableData->string(id: instructionLocation), args&: origintype), | 
| 145 |                     sendid); | 
| 146 |         return nullptr; | 
| 147 |     } | 
| 148 |  | 
| 149 |     QString invokeid; | 
| 150 |     if (stateMachine && stateMachine->isInvoked()) { | 
| 151 |         invokeid = stateMachine->sessionId(); | 
| 152 |     } | 
| 153 |  | 
| 154 |     QScxmlEvent *event = new QScxmlEvent; | 
| 155 |     event->setName(eventName); | 
| 156 |     event->setEventType(eventType); | 
| 157 |     event->setData(data); | 
| 158 |     event->setSendId(sendid); | 
| 159 |     event->setOrigin(origin); | 
| 160 |     event->setOriginType(origintype); | 
| 161 |     event->setInvokeId(invokeid); | 
| 162 |     return event; | 
| 163 | } | 
| 164 |  | 
| 165 | QScxmlEvent *QScxmlEventBuilder::errorEvent(QScxmlStateMachine *stateMachine, const QString &name, | 
| 166 |                                             const QString &message, const QString &sendid) | 
| 167 | { | 
| 168 |     QScxmlEventBuilder event; | 
| 169 |     event.stateMachine = stateMachine; | 
| 170 |     event.event = name; | 
| 171 |     event.eventType = QScxmlEvent::PlatformEvent; // Errors are platform events. See e.g. test331. | 
| 172 |     // _event.data == null, see test528 | 
| 173 |     event.id = sendid; | 
| 174 |     auto error = event(); | 
| 175 |     error->setErrorMessage(message); | 
| 176 |     return error; | 
| 177 | } | 
| 178 |  | 
| 179 | bool QScxmlEventBuilder::evaluate(const ParameterInfo ¶m, QScxmlStateMachine *stateMachine, | 
| 180 |                                   QVariantMap &keyValues) | 
| 181 | { | 
| 182 |     auto dataModel = stateMachine->dataModel(); | 
| 183 |     auto tableData = stateMachine->tableData(); | 
| 184 |     if (param.expr != NoEvaluator) { | 
| 185 |         bool success = false; | 
| 186 |         auto v = dataModel->evaluateToVariant(id: param.expr, ok: &success); | 
| 187 |         keyValues.insert(akey: tableData->string(id: param.name), avalue: v); | 
| 188 |         return success; | 
| 189 |     } | 
| 190 |  | 
| 191 |     QString loc; | 
| 192 |     if (param.location != QScxmlExecutableContent::NoString) { | 
| 193 |         loc = tableData->string(id: param.location); | 
| 194 |     } | 
| 195 |  | 
| 196 |     if (loc.isEmpty()) { | 
| 197 |         return false; | 
| 198 |     } | 
| 199 |  | 
| 200 |     if (dataModel->hasScxmlProperty(name: loc)) { | 
| 201 |         keyValues.insert(akey: tableData->string(id: param.name), avalue: dataModel->scxmlProperty(name: loc)); | 
| 202 |         return true; | 
| 203 |     } else { | 
| 204 |         submitError(QStringLiteral("error.execution" ), | 
| 205 |                     QStringLiteral("Error in <param>: %1 is not a valid location" ) | 
| 206 |                     .arg(a: loc)); | 
| 207 |         return false; | 
| 208 |     } | 
| 209 | } | 
| 210 |  | 
| 211 | bool QScxmlEventBuilder::evaluate(const QScxmlExecutableContent::Array<ParameterInfo> *params, | 
| 212 |                                   QScxmlStateMachine *stateMachine, QVariantMap &keyValues) | 
| 213 | { | 
| 214 |     if (!params) | 
| 215 |         return true; | 
| 216 |  | 
| 217 |     auto paramPtr = params->const_data(); | 
| 218 |     for (qint32 i = 0; i != params->count; ++i, ++paramPtr) { | 
| 219 |         if (!evaluate(param: *paramPtr, stateMachine, keyValues)) | 
| 220 |             return false; | 
| 221 |     } | 
| 222 |  | 
| 223 |     return true; | 
| 224 | } | 
| 225 |  | 
| 226 | void QScxmlEventBuilder::submitError(const QString &type, const QString &msg, const QString &sendid) | 
| 227 | { | 
| 228 |     QScxmlStateMachinePrivate::get(t: stateMachine)->submitError(type, msg, sendid); | 
| 229 | } | 
| 230 |  | 
| 231 | /*! | 
| 232 |  * \class QScxmlEvent | 
| 233 |  * \brief The QScxmlEvent class is an event for a Qt SCXML state machine. | 
| 234 |  * \since 5.7 | 
| 235 |  * \inmodule QtScxml | 
| 236 |  * | 
| 237 |  * SCXML \e events drive transitions. Most events are generated by using the | 
| 238 |  * \c <raise> and \c <send> elements in the application. The state machine | 
| 239 |  * automatically generates some mandatory events, such as errors. | 
| 240 |  * | 
| 241 |  * For more information, see | 
| 242 |  * \l {SCXML Specification - 5.10.1 The Internal Structure of Events}. | 
| 243 |  * For more information about how the Qt SCXML API differs from the | 
| 244 |  * specification, see \l {SCXML Compliance}. | 
| 245 |  * | 
| 246 |  * \sa QScxmlStateMachine | 
| 247 |  */ | 
| 248 |  | 
| 249 | /*! | 
| 250 |     \enum QScxmlEvent::EventType | 
| 251 |  | 
| 252 |     This enum type specifies the type of an SCXML event: | 
| 253 |  | 
| 254 |     \value  PlatformEvent | 
| 255 |             An event generated internally by the state machine. For example, | 
| 256 |             errors. | 
| 257 |     \value  InternalEvent | 
| 258 |             An event generated by a \c <raise> element. | 
| 259 |     \value  ExternalEvent | 
| 260 |             An event generated by a \c <send> element. | 
| 261 |  */ | 
| 262 |  | 
| 263 | /*! | 
| 264 |  * Creates a new external SCXML event. | 
| 265 |  */ | 
| 266 | QScxmlEvent::QScxmlEvent() | 
| 267 |     : d(new QScxmlEventPrivate) | 
| 268 | { } | 
| 269 |  | 
| 270 | /*! | 
| 271 |  * Destroys the SCXML event. | 
| 272 |  */ | 
| 273 | QScxmlEvent::~QScxmlEvent() | 
| 274 | { | 
| 275 |     delete d; | 
| 276 | } | 
| 277 |  | 
| 278 | /*! | 
| 279 |     \property QScxmlEvent::scxmlType | 
| 280 |     \brief The event type. | 
| 281 |  | 
| 282 | */ | 
| 283 |  | 
| 284 | /*! | 
| 285 |  * Returns the event type. | 
| 286 |  */ | 
| 287 | QString QScxmlEvent::scxmlType() const | 
| 288 | { | 
| 289 |     switch (d->eventType) { | 
| 290 |     case PlatformEvent: | 
| 291 |         return QLatin1String("platform" ); | 
| 292 |     case InternalEvent: | 
| 293 |         return QLatin1String("internal" ); | 
| 294 |     case ExternalEvent: | 
| 295 |         break; | 
| 296 |     } | 
| 297 |     return QLatin1String("external" ); | 
| 298 | } | 
| 299 |  | 
| 300 | /*! | 
| 301 |  * Clears the contents of the event. | 
| 302 |  */ | 
| 303 | void QScxmlEvent::clear() | 
| 304 | { | 
| 305 |     *d = QScxmlEventPrivate(); | 
| 306 | } | 
| 307 |  | 
| 308 | /*! | 
| 309 |  * Assigns \a other to this SCXML event and returns a reference to this SCXML | 
| 310 |  * event. | 
| 311 |  */ | 
| 312 | QScxmlEvent &QScxmlEvent::operator=(const QScxmlEvent &other) | 
| 313 | { | 
| 314 |     *d = *other.d; | 
| 315 |     return *this; | 
| 316 | } | 
| 317 |  | 
| 318 | /*! | 
| 319 |  * Constructs a copy of \a other. | 
| 320 |  */ | 
| 321 | QScxmlEvent::QScxmlEvent(const QScxmlEvent &other) | 
| 322 |     : d(new QScxmlEventPrivate(*other.d)) | 
| 323 | { | 
| 324 | } | 
| 325 |  | 
| 326 | /*! | 
| 327 |     \property QScxmlEvent::name | 
| 328 |  | 
| 329 |     \brief the name of the event. | 
| 330 |  | 
| 331 |     If the event is generated inside the SCXML document, this property holds the | 
| 332 |     value of the \e event attribute specified inside the \c <raise> or \c <send> | 
| 333 |     element. | 
| 334 |  | 
| 335 |     If the event is created in the C++ code and submitted to the | 
| 336 |     QScxmlStateMachine, the value of this property is matched against the value | 
| 337 |     of the \e event attribute specified inside the \c <transition> element in | 
| 338 |     the SCXML document. | 
| 339 | */ | 
| 340 |  | 
| 341 | /*! | 
| 342 |  * Returns the name of the event. | 
| 343 |  */ | 
| 344 | QString QScxmlEvent::name() const | 
| 345 | { | 
| 346 |     return d->name; | 
| 347 | } | 
| 348 |  | 
| 349 | /*! | 
| 350 |  * Sets the name of the event to \a name. | 
| 351 |  */ | 
| 352 | void QScxmlEvent::setName(const QString &name) | 
| 353 | { | 
| 354 |     d->name = name; | 
| 355 | } | 
| 356 |  | 
| 357 | /*! | 
| 358 |     \property QScxmlEvent::sendId | 
| 359 |  | 
| 360 |     \brief the ID of the event. | 
| 361 |  | 
| 362 |     The ID is used by the \c <cancel> element to identify the event to be | 
| 363 |     canceled. | 
| 364 |  | 
| 365 |     \note The state machine generates a unique ID if the \e id attribute is not | 
| 366 |     specified in the \c <send> element. The generated ID can be accessed through | 
| 367 |     this property. | 
| 368 | */ | 
| 369 |  | 
| 370 | /*! | 
| 371 |  * Returns the ID of the event. | 
| 372 |  */ | 
| 373 | QString QScxmlEvent::sendId() const | 
| 374 | { | 
| 375 |     return d->sendid; | 
| 376 | } | 
| 377 |  | 
| 378 | /*! | 
| 379 |  * Sets the ID \a sendid for this event. | 
| 380 |  */ | 
| 381 | void QScxmlEvent::setSendId(const QString &sendid) | 
| 382 | { | 
| 383 |     d->sendid = sendid; | 
| 384 | } | 
| 385 |  | 
| 386 | /*! | 
| 387 |     \property QScxmlEvent::origin | 
| 388 |  | 
| 389 |     \brief the URI that points to the origin of an SCXML event. | 
| 390 |  | 
| 391 |     The origin is equivalent to the \e target attribute of the \c <send> | 
| 392 |     element. | 
| 393 | */ | 
| 394 |  | 
| 395 | /*! | 
| 396 |  * Returns a URI that points to the origin of an SCXML event. | 
| 397 |  */ | 
| 398 | QString QScxmlEvent::origin() const | 
| 399 | { | 
| 400 |     return d->origin; | 
| 401 | } | 
| 402 |  | 
| 403 | /*! | 
| 404 |  * Sets the origin of an SCXML event to \a origin. | 
| 405 |  * | 
| 406 |  * \sa QScxmlEvent::origin | 
| 407 |  */ | 
| 408 | void QScxmlEvent::setOrigin(const QString &origin) | 
| 409 | { | 
| 410 |     d->origin = origin; | 
| 411 | } | 
| 412 |  | 
| 413 | /*! | 
| 414 |     \property QScxmlEvent::originType | 
| 415 |  | 
| 416 |     \brief the origin type of an SCXML event. | 
| 417 |  | 
| 418 |     The origin type is equivalent to the \e type attribute of the \c <send> | 
| 419 |     element. | 
| 420 | */ | 
| 421 |  | 
| 422 | /*! | 
| 423 |  * Returns the origin type of an SCXML event. | 
| 424 |  */ | 
| 425 | QString QScxmlEvent::originType() const | 
| 426 | { | 
| 427 |     return d->originType; | 
| 428 | } | 
| 429 |  | 
| 430 | /*! | 
| 431 |  * Sets the origin type of an SCXML event to \a origintype. | 
| 432 |  * | 
| 433 |  * \sa QScxmlEvent::originType | 
| 434 |  */ | 
| 435 | void QScxmlEvent::setOriginType(const QString &origintype) | 
| 436 | { | 
| 437 |     d->originType = origintype; | 
| 438 | } | 
| 439 |  | 
| 440 | /*! | 
| 441 |     \property QScxmlEvent::invokeId | 
| 442 |  | 
| 443 |     \brief the ID of the invoked state machine if the event is generated by one. | 
| 444 | */ | 
| 445 |  | 
| 446 | /*! | 
| 447 |  * If this event is generated by an invoked state machine, returns the ID of | 
| 448 |  * the \c <invoke> element. Otherwise, returns an empty value. | 
| 449 |  */ | 
| 450 | QString QScxmlEvent::invokeId() const | 
| 451 | { | 
| 452 |     return d->invokeId; | 
| 453 | } | 
| 454 |  | 
| 455 | /*! | 
| 456 |  * Sets the ID of an invoked state machine to \a invokeid. | 
| 457 |  * \sa QScxmlEvent::invokeId | 
| 458 |  */ | 
| 459 | void QScxmlEvent::setInvokeId(const QString &invokeid) | 
| 460 | { | 
| 461 |     d->invokeId = invokeid; | 
| 462 | } | 
| 463 |  | 
| 464 | /*! | 
| 465 |     \property QScxmlEvent::delay | 
| 466 |  | 
| 467 |     \brief The delay in milliseconds after which the event is to be delivered | 
| 468 |     after processing the \c <send> element. | 
| 469 | */ | 
| 470 |  | 
| 471 | /*! | 
| 472 |  * Returns the delay in milliseconds after which this event is to be delivered | 
| 473 |  * after processing the \c <send> element. | 
| 474 |  */ | 
| 475 | int QScxmlEvent::delay() const | 
| 476 | { | 
| 477 |     return d->delayInMiliSecs; | 
| 478 | } | 
| 479 |  | 
| 480 | /*! | 
| 481 |  * Sets the delay in milliseconds as the value of \a delayInMiliSecs. | 
| 482 |  * \sa QScxmlEvent::delay | 
| 483 |  */ | 
| 484 | void QScxmlEvent::setDelay(int delayInMiliSecs) | 
| 485 | { | 
| 486 |     d->delayInMiliSecs = delayInMiliSecs; | 
| 487 | } | 
| 488 | /*! | 
| 489 |     \property QScxmlEvent::eventType | 
| 490 |  | 
| 491 |     \brief the type of the event. | 
| 492 | */ | 
| 493 |  | 
| 494 | /*! | 
| 495 |  * Returns the type of this event. | 
| 496 |  * \sa QScxmlEvent::EventType | 
| 497 |  */ | 
| 498 | QScxmlEvent::EventType QScxmlEvent::eventType() const | 
| 499 | { | 
| 500 |     return d->eventType; | 
| 501 | } | 
| 502 |  | 
| 503 | /*! | 
| 504 |  * Sets the event type to \a type. | 
| 505 |  * \sa QScxmlEvent::eventType QScxmlEvent::EventType | 
| 506 |  */ | 
| 507 | void QScxmlEvent::setEventType(const EventType &type) | 
| 508 | { | 
| 509 |     d->eventType = type; | 
| 510 | } | 
| 511 |  | 
| 512 | /*! | 
| 513 |     \property QScxmlEvent::data | 
| 514 |  | 
| 515 |     \brief the data included by the sender. | 
| 516 |  | 
| 517 |     When \c <param> elements are used in the \c <send> element, the data will | 
| 518 |     contain a QVariantMap where the key is the \e name attribute, and the value | 
| 519 |     is taken from the \e expr attribute or the \e location attribute. | 
| 520 |  | 
| 521 |     When a \c <content> element is used, the data will contain a single item | 
| 522 |     with either the value of the \e expr attribute of the \c <content> element | 
| 523 |     or the child data of the \c <content> element. | 
| 524 | */ | 
| 525 |  | 
| 526 | /*! | 
| 527 |  * Returns the data included by the sender. | 
| 528 |  */ | 
| 529 | QVariant QScxmlEvent::data() const | 
| 530 | { | 
| 531 |     if (isErrorEvent()) | 
| 532 |         return QVariant(); | 
| 533 |     return d->data; | 
| 534 | } | 
| 535 |  | 
| 536 | /*! | 
| 537 |  * Sets the payload data to \a data. | 
| 538 |  * \sa QScxmlEvent::data | 
| 539 |  */ | 
| 540 | void QScxmlEvent::setData(const QVariant &data) | 
| 541 | { | 
| 542 |     if (!isErrorEvent()) | 
| 543 |         d->data = data; | 
| 544 | } | 
| 545 |  | 
| 546 | /*! | 
| 547 |     \property QScxmlEvent::errorEvent | 
| 548 |     \brief Whether the event represents an error. | 
| 549 | */ | 
| 550 |  | 
| 551 | /*! | 
| 552 |  * Returns \c true when this is an error event, \c false otherwise. | 
| 553 |  */ | 
| 554 | bool QScxmlEvent::isErrorEvent() const | 
| 555 | { | 
| 556 |     return eventType() == PlatformEvent && name().startsWith(QStringLiteral("error." )); | 
| 557 | } | 
| 558 |  | 
| 559 | /*! | 
| 560 |     \property QScxmlEvent::errorMessage | 
| 561 |     \brief An error message for an error event, or an empty QString. | 
| 562 | */ | 
| 563 |  | 
| 564 | /*! | 
| 565 |  * If this is an error event, returns the error message. Otherwise, returns an | 
| 566 |  *         empty QString. | 
| 567 |  */ | 
| 568 | QString QScxmlEvent::errorMessage() const | 
| 569 | { | 
| 570 |     if (!isErrorEvent()) | 
| 571 |         return QString(); | 
| 572 |     return d->data.toString(); | 
| 573 | } | 
| 574 |  | 
| 575 | /*! | 
| 576 |  * If this is an error event, the \a message is set as the error message. | 
| 577 |  */ | 
| 578 | void QScxmlEvent::setErrorMessage(const QString &message) | 
| 579 | { | 
| 580 |     if (isErrorEvent()) | 
| 581 |         d->data = message; | 
| 582 | } | 
| 583 |  | 
| 584 | QByteArray QScxmlEventPrivate::debugString(QScxmlEvent *event) | 
| 585 | { | 
| 586 |     if (event == nullptr) { | 
| 587 |         return "<null>" ; | 
| 588 |     } | 
| 589 |  | 
| 590 |     QJsonObject o; | 
| 591 |     if (!event->name().isNull()) | 
| 592 |         o[QStringLiteral("name" )] = event->name(); | 
| 593 |     if (!event->scxmlType().isNull()) | 
| 594 |         o[QStringLiteral("type" )] = event->scxmlType(); | 
| 595 |     if (!event->sendId().isNull()) | 
| 596 |         o[QStringLiteral("sendid" )] = event->sendId(); | 
| 597 |     if (!event->origin().isNull()) | 
| 598 |         o[QStringLiteral("origin" )] = event->origin(); | 
| 599 |     if (!event->originType().isNull()) | 
| 600 |         o[QStringLiteral("origintype" )] = event->originType(); | 
| 601 |     if (!event->invokeId().isNull()) | 
| 602 |         o[QStringLiteral("invokeid" )] = event->invokeId(); | 
| 603 |     if (!event->data().isNull()) | 
| 604 |         o[QStringLiteral("data" )] = QJsonValue::fromVariant(variant: event->data()); | 
| 605 |  | 
| 606 |     return QJsonDocument(o).toJson(format: QJsonDocument::Compact); | 
| 607 | } | 
| 608 |  | 
| 609 | QT_END_NAMESPACE | 
| 610 |  |