summaryrefslogtreecommitdiffstats
path: root/src/qscxml/scxmlevent.cpp
blob: 2034e63e4b24d90483fdf78b7d0a9d2de74a9db3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/****************************************************************************
 **
 ** Copyright (c) 2015 Digia Plc
 ** For any questions to Digia, please use contact form at http://qt.digia.com/
 **
 ** All Rights Reserved.
 **
 ** NOTICE: All information contained herein is, and remains
 ** the property of Digia Plc and its suppliers,
 ** if any. The intellectual and technical concepts contained
 ** herein are proprietary to Digia Plc
 ** and its suppliers and may be covered by Finnish and Foreign Patents,
 ** patents in process, and are protected by trade secret or copyright law.
 ** Dissemination of this information or reproduction of this material
 ** is strictly forbidden unless prior written permission is obtained
 ** from Digia Plc.
 ****************************************************************************/

#include "executablecontent_p.h"
#include "scxmlevent_p.h"

using namespace Scxml;

static bool evaluate(const ExecutableContent::Param &param, StateTable *table, QVariantList &dataValues, QStringList &dataNames)
{
    auto dataModel = table->dataModel();
    auto tableData = table->tableData();
    if (param.expr != NoEvaluator) {
        bool success = false;
        auto v = dataModel->evaluateToVariant(param.expr, &success);
        dataValues.append(v);
        dataNames.append(tableData->string(param.name));
        return success;
    }

    QString loc;
    if (param.location != ExecutableContent::NoString) {
        loc = tableData->string(param.location);
    }

    if (loc.isEmpty()) {
        return false;
    }

    if (dataModel->hasProperty(loc)) {
        dataValues.append(dataModel->property(loc));
        dataNames.append(tableData->string(param.name));
        return true;
    } else {
        table->submitError(QByteArray("error.execution"),
                           QStringLiteral("Error in <param>: %1 is not a valid location")
                           .arg(loc),
                           /*sendid =*/ QByteArray());
        return false;
    }
}

static bool evaluate(const ExecutableContent::Array<ExecutableContent::Param> *params, StateTable *table, QVariantList &dataValues, QStringList &dataNames)
{
    if (!params)
        return true;

    auto paramPtr = params->const_data();
    for (qint32 i = 0; i != params->count; ++i, ++paramPtr) {
        if (!evaluate(*paramPtr, table, dataValues, dataNames))
            return false;
    }

    return true;
}

QAtomicInt EventBuilder::idCounter = QAtomicInt(0);

ScxmlEvent *EventBuilder::buildEvent()
{
    auto dataModel = table ? table->dataModel() : nullptr;
    auto tableData = table ? table->tableData() : nullptr;

    QByteArray eventName = event;
    bool ok = true;
    if (eventexpr != NoEvaluator) {
        eventName = dataModel->evaluateToString(eventexpr, &ok).toUtf8();
        ok = true; // ignore failure.
    }

    QVariantList dataValues;
    QStringList dataNames;
    if ((!params || params->count == 0) && (!namelist || namelist->count == 0)) {
        QVariant data;
        if (contentExpr != NoEvaluator) {
            data = dataModel->evaluateToString(contentExpr, &ok);
        } else {
            data = contents;
        }
        if (ok) {
            dataValues.append(data);
        } else {
            // expr evaluation failure results in the data property of the event being set to null. See e.g. test528.
            dataValues = { QVariant(QMetaType::VoidStar, 0) };
        }
    } else {
        if (evaluate(params, table, dataValues, dataNames)) {
            if (namelist) {
                for (qint32 i = 0; i < namelist->count; ++i) {
                    QString name = tableData->string(namelist->const_data()[i]);
                    dataNames << name;
                    dataValues << dataModel->property(name);
                }
            }
        } else {
            // If the evaluation of the <param> tags fails, set _event.data to an empty string.
            // See test343.
            dataValues = { QVariant(QMetaType::VoidStar, 0) };
            dataNames.clear();
        }
    }

    QByteArray sendid = id;
    if (!idLocation.isEmpty()) {
        sendid = generateId();
        table->dataModel()->setStringProperty(idLocation, QString::fromUtf8(sendid), tableData->string(instructionLocation), &ok);
        if (!ok)
            return nullptr;
    }

    QString origin = target;
    if (targetexpr != NoEvaluator) {
        origin = dataModel->evaluateToString(targetexpr, &ok);
        if (!ok)
            return nullptr;
    }
    if (origin.isEmpty()) {
        if (eventType == ScxmlEvent::External) {
            origin = QStringLiteral("#_internal");
        }
    } else if (!table->isLegalTarget(origin)) {
        // [6.2.4] and test194.
        table->submitError(QByteArray("error.execution"),
                           QStringLiteral("Error in %1: %2 is not a legal target")
                           .arg(tableData->string(instructionLocation), origin),
                           sendid);
        return nullptr;
    } else if (!table->isDispatchableTarget(origin)) {
        // [6.2.4] and test521.
        table->submitError(QByteArray("error.communication"),
                           QStringLiteral("Error in %1: cannot dispatch to target '%2'")
                           .arg(tableData->string(instructionLocation), origin),
                           sendid);
        return nullptr;
    }

    QString origintype = type;
    if (origintype.isEmpty()) {
        // [6.2.5] and test198
        origintype = QStringLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor");
    }
    if (typeexpr != NoEvaluator) {
        origintype = dataModel->evaluateToString(typeexpr, &ok);
        if (!ok)
            return nullptr;
    }
    if (!origintype.isEmpty() && origintype != QStringLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor")) {
        // [6.2.5] and test199
        table->submitError(QByteArray("error.execution"),
                           QStringLiteral("Error in %1: %2 is not a valid type")
                           .arg(tableData->string(instructionLocation), origintype),
                           sendid);
        return nullptr;
    }

    return new ScxmlEvent(eventName, eventType, dataValues, dataNames, sendid, origin, origintype);
}