1 | // Copyright (C) 2021 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 "qtypedjson_p.h" |
5 | #include <QtCore/QLoggingCategory> |
6 | #include <QtCore/qjsondocument.h> |
7 | #include <QtCore/qjsonvalue.h> |
8 | #include <QtCore/qjsonarray.h> |
9 | #include <cstring> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | namespace QTypedJson { |
14 | |
15 | Q_LOGGING_CATEGORY(jsonRpcLog, "qt.jsonrpc" ); |
16 | |
17 | Reader::Reader(const QJsonValue &v) |
18 | : m_p(new ReaderPrivate { .valuesStack: QList({ ValueStack { .value: v, .fieldPath: QString(), .indexPath: -1, .warnLevel: 0 } }) }) |
19 | { |
20 | } |
21 | |
22 | Reader::~Reader() |
23 | { |
24 | for (const QString &msg : m_p->errorMessages) |
25 | qCWarning(jsonRpcLog) << msg; |
26 | delete m_p; |
27 | } |
28 | |
29 | QStringList Reader::errorMessages() |
30 | { |
31 | return m_p->errorMessages; |
32 | } |
33 | |
34 | void Reader::clearErrorMessages() |
35 | { |
36 | m_p->errorMessages.clear(); |
37 | } |
38 | |
39 | void Reader::handleBasic(bool &el) |
40 | { |
41 | if (currentValue().isBool()) |
42 | el = currentValue().toBool(); |
43 | else |
44 | warnMissing(s: u"bool" ); |
45 | } |
46 | |
47 | void Reader::handleBasic(QByteArray &el) |
48 | { |
49 | if (currentValue().isString()) |
50 | el = currentValue().toString().toUtf8(); |
51 | else |
52 | warnMissing(s: u"string" ); |
53 | } |
54 | |
55 | void Reader::handleBasic(int &el) |
56 | { |
57 | if (currentValue().isDouble()) |
58 | el = currentValue().toInt(defaultValue: el); |
59 | else |
60 | warnMissing(s: u"int" ); |
61 | } |
62 | |
63 | void Reader::handleBasic(double &el) |
64 | { |
65 | if (currentValue().isDouble()) |
66 | el = currentValue().toDouble(); |
67 | else |
68 | warnMissing(s: u"double" ); |
69 | } |
70 | |
71 | void Reader::handleNullType() |
72 | { |
73 | if (!currentValue().isNull() && !currentValue().isUndefined()) { |
74 | warnNonNull(); |
75 | } |
76 | } |
77 | |
78 | bool Reader::startField(const QString &fieldName) |
79 | { |
80 | int oldWarnLevel = (m_p->valuesStack.isEmpty() ? 0 : m_p->valuesStack.last().warnLevel); |
81 | m_p->objectsStack.last().visitedFields.insert(value: fieldName); |
82 | m_p->valuesStack.append(t: ValueStack { .value: currentValue()[fieldName], .fieldPath: fieldName, .indexPath: -1, |
83 | .warnLevel: (oldWarnLevel ? oldWarnLevel + 1 : 0) }); |
84 | return true; |
85 | } |
86 | |
87 | bool Reader::startField(const char *fieldName) |
88 | { |
89 | QString f = QString::fromUtf8(utf8: fieldName); // conversion needed just to set the fieldPath |
90 | return startField(fieldName: f); |
91 | } |
92 | |
93 | void Reader::endField(const QString &fieldName) |
94 | { |
95 | Q_ASSERT(m_p->valuesStack.last().fieldPath == fieldName); |
96 | m_p->valuesStack.removeLast(); |
97 | } |
98 | |
99 | void Reader::endField(const char *fieldName) |
100 | { |
101 | QString f = QString::fromUtf8(utf8: fieldName); |
102 | endField(fieldName: f); |
103 | } |
104 | |
105 | bool Reader::startObjectF(const char *type, ObjectOptions options, quintptr) |
106 | { |
107 | if (m_p->parseStatus != ParseStatus::Normal) |
108 | return false; |
109 | if (currentValue().isUndefined()) |
110 | return false; |
111 | m_p->objectsStack.append(t: ObjectStack { .type: type, .options: options, .visitedFields: {} }); |
112 | return true; |
113 | } |
114 | |
115 | void Reader::endObjectF(const char *type, ObjectOptions, quintptr) |
116 | { |
117 | Q_ASSERT(std::strcmp(m_p->objectsStack.last().type, type) == 0); |
118 | m_p->objectsStack.removeLast(); |
119 | } |
120 | |
121 | void Reader::(const QJsonObject &e) |
122 | { |
123 | if (e.constBegin() != e.constEnd()) |
124 | warn(QStringLiteral(u"%1 has extra fields %2" ) |
125 | .arg(args: currentPath(), args: QString::fromUtf8(ba: QJsonDocument(e).toJson()))); |
126 | } |
127 | |
128 | void Reader::warnInvalidSize(qint32 size, qint32 expectedSize) |
129 | { |
130 | if (size != expectedSize) |
131 | warn(QStringLiteral(u"%1 expected %1 elements, not %2." ) |
132 | .arg(args: currentPath(), args: QString::number(expectedSize), args: QString::number(size))); |
133 | } |
134 | |
135 | void Reader::warnMissing(QStringView s) |
136 | { |
137 | warn(QStringLiteral(u"%1 misses value of type %2" ).arg(args: currentPath(), args&: s)); |
138 | } |
139 | |
140 | void Reader::warnNonNull() |
141 | { |
142 | QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson(); |
143 | warn(QStringLiteral(u"%1 is supposed to be null, but is %2" ) |
144 | .arg(args: currentPath(), args: QString::fromUtf8(ba: val.mid(index: 1, len: val.size() - 2)))); |
145 | } |
146 | |
147 | void Reader::warn(const QString &msg) |
148 | { |
149 | m_p->errorMessages.append(t: msg); |
150 | m_p->parseStatus = ParseStatus::Failed; |
151 | } |
152 | |
153 | void Reader::handleJson(QJsonValue &v) |
154 | { |
155 | v = currentValue(); |
156 | } |
157 | |
158 | void Reader::handleJson(QJsonObject &v) |
159 | { |
160 | if (!currentValue().isObject() && !currentValue().isNull() && !currentValue().isUndefined()) { |
161 | QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson(); |
162 | warn(QStringLiteral(u"Error: expected an object at %1, not %2" ) |
163 | .arg(args: currentPath(), args: QString::fromUtf8(ba: val.mid(index: 1, len: val.size() - 2)))); |
164 | } |
165 | v = currentValue().toObject(); |
166 | } |
167 | |
168 | void Reader::handleJson(QJsonArray &v) |
169 | { |
170 | if (!currentValue().isArray() && !currentValue().isNull() && !currentValue().isUndefined()) { |
171 | QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson(); |
172 | warn(QStringLiteral(u"Error: expected an array at %1, not %2" ) |
173 | .arg(args: currentPath(), args: QString::fromUtf8(ba: val.mid(index: 1, len: val.size() - 2)))); |
174 | } |
175 | v = currentValue().toArray(); |
176 | } |
177 | |
178 | QJsonObject Reader::() const |
179 | { |
180 | QJsonObject ; |
181 | QJsonObject v = currentValue().toObject(); |
182 | auto it = v.constBegin(); |
183 | auto end = v.constEnd(); |
184 | auto &vField = m_p->objectsStack.last().visitedFields; |
185 | while (it != end) { |
186 | if (!vField.contains(value: it.key())) { |
187 | extraFields.insert(key: it.key(), value: it.value()); |
188 | } |
189 | ++it; |
190 | } |
191 | return extraFields; |
192 | } |
193 | |
194 | void Reader::startArrayF(qint32 &size) |
195 | { |
196 | size = int(currentValue().toArray().size()); |
197 | } |
198 | |
199 | bool Reader::startElement(qint32 index) |
200 | { |
201 | int oldWarnLevel = (m_p->valuesStack.isEmpty() ? 0 : m_p->valuesStack.last().warnLevel); |
202 | m_p->valuesStack.append(t: ValueStack { .value: currentValue().toArray().at(i: index), .fieldPath: QString(), .indexPath: index, |
203 | .warnLevel: (oldWarnLevel ? oldWarnLevel + 1 : 0) }); |
204 | return true; |
205 | } |
206 | |
207 | void Reader::endElement(qint32 index) |
208 | { |
209 | Q_ASSERT(m_p->valuesStack.last().indexPath == index); |
210 | m_p->valuesStack.removeLast(); |
211 | } |
212 | |
213 | void Reader::endArrayF(qint32 &) { } |
214 | |
215 | QString Reader::currentPath() const |
216 | { |
217 | QStringList res; |
218 | for (const auto &el : std::as_const(t&: m_p->valuesStack)) { |
219 | if (el.indexPath != -1) |
220 | res.append(t: QString::number(el.indexPath)); |
221 | else |
222 | res.append(t: el.fieldPath); |
223 | } |
224 | return res.join(sep: u"." ); |
225 | } |
226 | |
227 | bool Reader::startTuple(qint32 size) |
228 | { |
229 | qint32 expected = qint32(currentValue().toArray().size()); |
230 | if (size != expected) { |
231 | warnInvalidSize(size, expectedSize: expected); |
232 | return false; |
233 | }; |
234 | return true; |
235 | } |
236 | |
237 | void Reader::endTuple(qint32) { } |
238 | |
239 | void JsonBuilder::handleBasic(const bool &v) |
240 | { |
241 | m_values.append(t: QJsonValue(v)); |
242 | } |
243 | |
244 | void JsonBuilder::handleBasic(const QByteArray &v) |
245 | { |
246 | m_values.append(t: QJsonValue(QString::fromUtf8(ba: v))); |
247 | } |
248 | |
249 | void JsonBuilder::handleBasic(const int &v) |
250 | { |
251 | m_values.append(t: QJsonValue(v)); |
252 | } |
253 | |
254 | void JsonBuilder::handleBasic(const double &v) |
255 | { |
256 | m_values.append(t: QJsonValue(v)); |
257 | } |
258 | |
259 | void JsonBuilder::handleNullType() |
260 | { |
261 | m_values.append(t: QJsonValue(QJsonValue::Type::Null)); |
262 | } |
263 | |
264 | void JsonBuilder::handleMissingOptional() |
265 | { |
266 | if (m_fieldLevel.isEmpty() || m_fieldLevel.last() != m_values.size()) |
267 | handleNullType(); |
268 | } |
269 | |
270 | bool JsonBuilder::startField(const QString &) |
271 | { |
272 | m_fieldLevel.append(t: m_values.size()); |
273 | return true; |
274 | } |
275 | |
276 | bool JsonBuilder::startField(const char *) |
277 | { |
278 | m_fieldLevel.append(t: m_values.size()); |
279 | return true; |
280 | } |
281 | |
282 | void JsonBuilder::endField(const QString &v) |
283 | { |
284 | Q_ASSERT(!m_fieldLevel.isEmpty()); |
285 | if (m_fieldLevel.last() < m_values.size()) { |
286 | Q_ASSERT(m_values.size() > 1); |
287 | if (QJsonObject *o = std::get_if<QJsonObject>(ptr: &m_values[m_values.size() - 2])) { |
288 | o->insert(key: v, value: popLastValue()); |
289 | } else { |
290 | Q_ASSERT(false); |
291 | } |
292 | } |
293 | Q_ASSERT(!m_fieldLevel.isEmpty() && m_fieldLevel.last() == m_values.size()); |
294 | m_fieldLevel.removeLast(); |
295 | } |
296 | |
297 | void JsonBuilder::endField(const char *v) |
298 | { |
299 | endField(v: QString::fromUtf8(utf8: v)); |
300 | } |
301 | |
302 | bool JsonBuilder::startObjectF(const char *, ObjectOptions, quintptr) |
303 | { |
304 | m_values.append(t: QJsonObject()); |
305 | return true; |
306 | } |
307 | |
308 | void JsonBuilder::endObjectF(const char *, ObjectOptions, quintptr) { } |
309 | |
310 | bool JsonBuilder::startArrayF(qint32 &) |
311 | { |
312 | m_values.append(t: QJsonArray()); |
313 | m_arrayLevel.append(t: m_values.size()); |
314 | return true; |
315 | } |
316 | |
317 | bool JsonBuilder::startElement(qint32) |
318 | { |
319 | return true; |
320 | } |
321 | |
322 | void JsonBuilder::endElement(qint32) |
323 | { |
324 | Q_ASSERT(m_values.size() > 1); |
325 | if (QJsonArray *a = std::get_if<QJsonArray>(ptr: &m_values[m_values.size() - 2])) { |
326 | a->append(value: popLastValue()); |
327 | } else { |
328 | Q_ASSERT(false); |
329 | } |
330 | } |
331 | |
332 | void JsonBuilder::endArrayF(qint32 &) |
333 | { |
334 | Q_ASSERT(!m_arrayLevel.isEmpty() && m_arrayLevel.last() == m_values.size()); |
335 | m_arrayLevel.removeLast(); |
336 | } |
337 | |
338 | void JsonBuilder::handleJson(QJsonValue &v) |
339 | { |
340 | m_values.append(t: v); |
341 | } |
342 | |
343 | void JsonBuilder::handleJson(QJsonObject &v) |
344 | { |
345 | m_values.append(t: v); |
346 | } |
347 | |
348 | void JsonBuilder::handleJson(QJsonArray &v) |
349 | { |
350 | m_values.append(t: v); |
351 | } |
352 | |
353 | QJsonValue JsonBuilder::popLastValue() |
354 | { |
355 | if (m_values.isEmpty()) |
356 | return QJsonValue(QJsonValue::Type::Undefined); |
357 | QJsonValue res = std::visit(visitor: [](auto &v) { return QJsonValue(v); }, variants&: m_values.last()); |
358 | m_values.removeLast(); |
359 | return res; |
360 | } |
361 | |
362 | bool JsonBuilder::startTuple(qint32 size) |
363 | { |
364 | return startArrayF(size); |
365 | } |
366 | |
367 | void JsonBuilder::endTuple(qint32 size) |
368 | { |
369 | endArrayF(size); |
370 | } |
371 | |
372 | } // namespace QTypedJson |
373 | |
374 | QT_END_NAMESPACE |
375 | |