Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

QJson_impl.h 34.5 KB
Newer Older
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
/****************************************************************************
 * **
 * ** Copyright (C) 2017 MinMaxMedical.
 * ** All rights reserved.
 * ** Contact: MinMaxMedical <InCAS@MinMaxMedical.com>
 * **
 * ** This file is part of the modmedLog module.
 * **
 * ** $QT_BEGIN_LICENSE:BSD$
 * ** You may use this file under the terms of the BSD license as follows:
 * **
 * ** "Redistribution and use in source and binary forms, with or without
 * ** modification, are permitted provided that the following conditions are
 * ** met:
 * **   * Redistributions of source code must retain the above copyright
 * **     notice, this list of conditions and the following disclaimer.
 * **   * Redistributions in binary form must reproduce the above copyright
 * **     notice, this list of conditions and the following disclaimer in
 * **     the documentation and/or other materials provided with the
 * **     distribution.
 * **   * Neither the name of MinMaxMedical S.A.S. and its Subsidiary(-ies) nor
 * **     the names of its contributors may be used to endorse or promote
 * **     products derived from this software without specific prior written
 * **     permission.
 * **
 * ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 * ** $QT_END_LICENSE$
 * **
 * ****************************************************************************/
#pragma once

#include <type_traits>

#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qiodevice.h>
#include <QtCore/qstack.h>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
50
#include "QValue.h"
51
52

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
53
// QTransmogrifier<T,QJson*> support
54

55
class QJsonBuilder : public QAbstractValueWriter
56
57
58
59
{
    Q_DISABLE_COPY(QJsonBuilder)
public:
    QJsonBuilder(QJsonValue* v) : json(v) { Q_ASSERT(v); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
60
    void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); }
61
62

    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
63
64
65
    /**/                 QValue       value   (                  ) { return QValueStatus(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
    template<typename T> QValueStatus bind    (             T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
66
protected:
67
    friend class QJsonReader; //!< Calls methods below for out-of-order cachedItems
68
69
    bool trySequence(quint32* s=nullptr) { Q_UNUSED(s); steps.push(Step(                 )); return true; }
    bool tryRecord  (quint32* s=nullptr) { Q_UNUSED(s); steps.push(Step(qBindExpectedItem)); return true; }
70
71
    bool tryAny     (                  ) { set(QJsonValue(QJsonValue::Undefined)); return true; }
    bool tryNull    (                  ) { set(QJsonValue(QJsonValue::Null     )); return true; }
72
73
74
75
76
77
78
79
80
    bool tryBind    (   QUtf8DataView u) { set(QJsonValue(u.data())); return true; }
    bool tryBind    (          bool&& b) { set(QJsonValue(b       )); return true; }
    bool tryBind    (        double&& d) { set(QJsonValue(d       )); return true; }
    bool tryBind    (       quint64&& n) { return tryBind(double(n)); }
    bool tryBind    (        qint64&& n) { return tryBind(double(n)); }

    bool tryItem(QIdentifier& n) { steps.last().key=n            ; return true; }
    bool tryItem(              ) { steps.last().key=QIdentifier(); return true; }
    bool tryOut (              ) { auto level = steps.pop(); set(!level.key.isNull() ? QJsonValue(level.object) : QJsonValue(level.array)); return true; }
81
82
private:
    void set(const QJsonValue& v) {
83
        if (steps.isEmpty()) {
84
85
86
            *json = v;
        }
        else {
87
            if (!steps.last().key.isNull())
88
                steps.last().object[steps.last().key.latin1()]=v;
89
            else
90
                steps.last().array.append(v);
91
92
93
94
        }
    }

    QJsonValue* json;
95
    struct Step { QIdentifier key; /* TODO union */ QJsonObject object; QJsonArray array; Step(QIdentifier k=QIdentifier()) : key(k) {} };
96
    QStack<Step> steps = QStack<Step>(); //!< minimal dynamic context to implement out() and ensure actual building in case QJsonBuilderImpl is abandoned
97
98
99
100
};

// --------------------------------------------------------------------------

101
class QJsonVisitor : public QAbstractValueReader
102
103
104
105
{
    Q_DISABLE_COPY(QJsonVisitor)
public:
    QJsonVisitor(const QJsonValue* v) : json(v) { Q_ASSERT(v); }
106
    void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); }
107

108
    QAsciiData currentPath() {
109
        QByteArray path;
110
        Q_FOREACH(Step s, steps) {
111
112
            if (!s.key.isNull()) { path.append('{').append(                   s.key.utf8() ); }
            else                 { path.append('[').append(QByteArray::number(s.idx       )); }
113
        }
114
        return QAsciiData(path);
115
116
117
    }

    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
118
119
120
    /**/                 QValue       value   (                  ) { return QValueStatus(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
    template<typename T> QValueStatus bind    (             T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
121
protected:
122
123
    bool trySequence(quint32* =nullptr) {            if (current().isArray ()) {       steps.push(Step()); return true; } handleError(qBindExpectedSequence); return false; }
    bool tryRecord  (quint32* =nullptr) {            if (current().isObject()) {       steps.push(Step()); return true; } handleError(qBindExpectedRecord  ); return false; }
124
125
    bool tryAny     (                 ) {            if (current().isUndefined()) {                        return true; } handleError(qBindUnexpectedValue ); return false; }
    bool tryNull    (                 ) {            if (current().isNull()  ) {                           return true; } handleError(qBindExpectedNull    ); return false; }
126
127
128
129
130
131
132
    bool tryBind    (     QUtf8Data& u) { QString s; if (tryBind(s)          ) { u = s        .toUtf8  (); return true; }                                     return false; }
    bool tryBind    (       QString& v) {            if (current().isString()) { v = current().toString(); return true; } handleError(qBindExpectedText    ); return false; }
    bool tryBind    (          bool& v) {            if (current().isBool  ()) { v = current().toBool  (); return true; } handleError(qBindExpectedBoolean ); return false; }
    bool tryBind    (        qint64& t) {  double d; if (tryBind(d)          ) { t =  qint64(d)          ; return true; }                                     return false; }
    bool tryBind    (       quint64& t) {  double d; if (tryBind(d)          ) { t = quint64(d)          ; return true; }                                     return false; }
    bool tryBind    (         float& v) {  double d; if (tryBind(d)          ) { v = float(d)            ; return true; }                                     return false; }
    bool tryBind    (        double& v) {            if (current().isDouble()) { v = current().toDouble(); return true; } handleError(qBindExpectedDecimal ); return false; }
133
134
135
136
137

    bool tryItem(QIdentifierLiteral u) { steps.last().key=u; return !(steps.last().item = current(1).toObject().value(steps.last().key.latin1())).isUndefined(); }
    bool tryItem(QIdentifier&       k) { steps.last().key=k; return !(steps.last().item = current(1).toObject().value(steps.last().key.latin1())).isUndefined(); }
    bool tryItem(                    ) { steps.last().idx++; return !(steps.last().item = current(1).toArray ().   at(steps.last().idx         )).isUndefined(); }
    bool tryOut (                    ) { steps.removeLast(); return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
138

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
139
    bool isValid() const noexcept { return true; }
140
    bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
141
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
142
    const QJsonValue& current(int outer=0) const { Q_ASSERT(0<=outer && json); return steps.size()-outer <= 0 ? *json : steps[steps.size()-outer-1].item; }
143
144

    const QJsonValue* json;
145
    struct Step { QIdentifier key; int idx=-1; QJsonValue item; Step() = default; };
146
147
148
149
150
    QStack<Step> steps = QStack<Step>();
};

// --------------------------------------------------------------------------

151
class QJsonWriter : public QAbstractValueWriter
152
153
{
    Q_DISABLE_COPY(QJsonWriter)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
154
    Q_ENABLE_MOVE_DEFAULT(QJsonWriter)
155
public:
156
157
158
    QJsonWriter( QIODevice* io) : io(io) { Q_ASSERT(io || ba); }
    QJsonWriter(QByteArray* ba) : ba(ba) { Q_ASSERT(io || ba); }
   ~QJsonWriter() { while (!levels.isEmpty()) write(levels.takeLast().end); }
159
160

    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
161
162
163
    /**/                 QValue       value   (                  ) { return QValueStatus(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
    template<typename T> QValueStatus bind    (             T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
164
protected:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
165
166
    template<class T> friend class QModelWriter;

167
168
    bool trySequence(quint32* s=nullptr) { Q_UNUSED(s); levels.push(Step{"","]"}); return putChar('['); }
    bool tryRecord  (quint32* s=nullptr) { Q_UNUSED(s); levels.push(Step{"","}"}); return putChar('{'); }
169
    bool tryAny     (                  ) { return handleError(qBindUnexpectedValue) && write("undefined"); }
170
171
    bool tryNull    (                  ) { return write("null"); }
    bool tryBind    (   QUtf8DataView u) { return putString(u.data(), u.size()); }
172
    // JSON literals
173
174
175
176
177
178
179
180
181
182
    bool tryBind    (          bool&& n) { return write(                   n?"true":"false"       ); }
    bool tryBind    (         float&& n) { return write(QByteArray::number(n,'g',std::numeric_limits< float>::max_digits10)); }
    bool tryBind    (        double&& n) { return write(QByteArray::number(n,'g',std::numeric_limits<double>::max_digits10)); }
    bool tryBind    (       quint64&& t) { return write(QByteArray::number(t)); }
    bool tryBind    (        qint64&& t) { return write(QByteArray::number(t)); }

    bool tryItem(QIdentifier&       n) { auto r=(write(levels.last().sep) || *levels.last().sep=='\0') && putString(n.data(), n.size()) && putChar(':'); levels.last().sep = ","; return r; }
    bool tryItem(QIdentifierLiteral n) { auto r=(write(levels.last().sep) || *levels.last().sep=='\0') && putString(n.data(), n.size()) && putChar(':'); levels.last().sep = ","; return r; }
    bool tryItem(                    ) { auto r=(write(levels.last().sep) || *levels.last().sep=='\0')                                                 ; levels.last().sep = ","; return r; }
    bool tryOut (                    ) { return  write(levels.pop() .end); }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
private:
    struct Step { const char* sep; const char* end; };

    bool write    (const char* utf8) { if (ba) { ba->append( utf8); return true; } else return io->write  ( utf8); }
    bool putChar  (      char ascii) { if (ba) { ba->append(ascii); return true; } else return io->putChar(ascii); }
    bool putString(const char* utf8, int size)
    {
        putChar('"');
        for (int i=0 ; i<size ; ++i)
        {
            /**/ if (utf8[i] == '\\') { putChar('\\'); putChar('\\'); }
            else if (utf8[i] ==  '"') { putChar('\\'); putChar( '"'); }
            else if (utf8[i] <    0|| // UTF-8 sequence bytes
                     utf8[i] >=  ' ') { putChar(utf8[i]); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
197
198
199
200
201
202
203
204
205
206
207
208
209
210
            else                      {
                                        putChar('\\'); switch (utf8[i]) {
                                           case '\t' : putChar( 't'); break;
                                           case '\b' : putChar( 'b'); break;
                                           case '\f' : putChar( 'f'); break;
                                           case '\n' : putChar( 'n'); break;
                                           case '\r' : putChar( 'r'); break;
                                           default   : putChar( 'u'); {
                                              char high=((utf8[i]) / 16), low=((utf8[i]) % 16);
                                              putChar(              '0'        );
                                              putChar(              '0'        );
                                              putChar(high+(high<10?'0':'A'-10));
                                              putChar( low+( low<10?'0':'A'-10)); }    break;
                                           }
211
212
            }
        }
213
        return putChar('"');
214
215
    }

216
217
218
    QIODevice*   io     = nullptr;
    QByteArray*  ba     = nullptr;
    QStack<Step> levels = QStack<Step>(); // std::vector<bool> does not increase performance here
219
220
221
222
};

// --------------------------------------------------------------------------

223
#include "QVariant_impl.h" // as caching value reader and writer for out-of-order item keys
224

225
class QJsonReader : public QAbstractValueReader
226
227
228
{
    Q_DISABLE_COPY(QJsonReader)
public:
229
    QJsonReader(QIODevice* io) : io(io), cacheWriter(&cachedValue), cacheReader(&cachedValue) { Q_ASSERT(io); }
230
231
232
233
234
235
236
237
238
239
240
    void reset(QIODevice* other) {
        errors.clear(); levels.clear();
        line=0; column=0; index=-1;
        cachedNumber = None; d=.0; i=0; neg=bool();
        cacheLevel = 0; cachedValue = QJsonValue();
        cacheWriter.reset(&cachedValue);
        caching = nullptr; //!< Only used when cacheLevel>=1 && key!=expectedKey
        cacheReader.reset(&cachedValue);
        io=other;
        Q_ASSERT(io);
    }
241

242
    struct Step  { int index; const char* end; QMap<QIdentifier,QJsonValue/*TODO QVariant for meta() support*/> cachedItems; Step(int i=-1, const char* e=nullptr) : index(i), end(e) {} };
243
    struct Error { QIdentifierLiteral error; int line; int column; int index; QValueEnd zap(QValue&& value) { QByteArray u(error.utf8()); u.append(' ').append(QByteArray::number(line)).append(':').append(QByteArray::number(column)); return value.bind(QUtf8Data(u)); } };
244
245
246
    QVector<Error> errors;

    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
247
248
249
    /**/                 QValue       value   (                  ) { return QValueStatus(this).value(); }
    /**/                 QSequence    sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
    template<typename T> QValueStatus bind    (             T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
250
protected:
251
    enum CachedNumber : quint8 { None=0, Integer, FloatingPoint };
252

253
    bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); }
254
                                           if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } handleError(qBindExpectedSequence); return false; }
255
    bool tryRecord  (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); }
256
                                           if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } handleError(qBindExpectedRecord  ); return false; }
257
    bool tryAny     (                  ) { return next(",}]"); }
258
    bool tryNull    (                  ) { if (caching) { return caching->tryNull() && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
259
                                           if (get('n', "[{\"ntf-0123456789.") &&
260
261
262
                                               get('u') &&
                                               get('l') &&
                                               get('l')) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
263
                                               return true;
264
                                           } else { handleError(qBindExpectedNull); return false; } }
265
    bool tryBind    (      QUtf8Data& s) { if (caching) { return caching->tryBind(s) && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
266
267
268
269
270
                                           QByteArray u;
                                           if (          get('"', "[{\"ntf-0123456789.")) { u.resize(0); char c;
                                               while ((c=getCharInString()) != '\0'     ) { u.append(c); }
                                               s=QUtf8Data(u);
                                               return    get('"');
271
                                           } else { handleError(qBindExpectedText); return false; } }
272
    bool tryBind    (        QString& s) { QUtf8Data u; if (tryBind(u)) { s = QString(u.utf8()); return true; } return false; }
273
    bool tryBind    (           bool& b) { if (caching) { return caching->tryBind(b) && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
274
                                           if (get('t', "[{\"ntf-0123456789.") &&
275
276
277
                                               get('r') &&
                                               get('u') &&
                                               get('e')) { b=true;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
278
279
280
                                               return true;
                                           } else
                                           if (get('f', "[{\"ntf-0123456789.") &&
281
282
283
284
                                               get('a') &&
                                               get('l') &&
                                               get('s') &&
                                               get('e')) { b=false;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
285
                                               return true;
286
                                           } else { handleError(qBindExpectedBoolean); return false; } }
287
    bool tryBind    (          float& n) { if (caching) { return caching->tryBind(n) && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
288
289
                                           if (getNumber()==None) return false; // already reported qBindExpectedDecimal
                                           if (d<double( std::numeric_limits<float >::min())||
290
                                                 double( std::numeric_limits<float >::max())<d) { handleError(qBindExpectedSmallerNumber); return false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
291
                                           n = float(d); cachedNumber=None; return true; }
292
    bool tryBind    (         double& n) { if (caching) { return caching->tryBind(n) && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
293
294
                                           if (getNumber()==None) return false; // already reported qBindExpectedDecimal
                                           n = d ; cachedNumber=None; return true; }
295
    bool tryBind    (        quint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
296
297
                                           auto r=getNumber();
                                           if (r==None) return false; // already reported qBindExpectedDecimal
298
299
                                           if (r==FloatingPoint) { handleError(qBindExpectedInteger); return false; }
                                           if (neg) { handleError(qBindExpectedPositiveInteger); return false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
300
                                           t = i ; cachedNumber=None; return true; }
301
    bool tryBind    (         qint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
302
303
                                           auto r=getNumber();
                                           if (r==None) return false; // already reported qBindExpectedDecimal
304
                                           if (r==FloatingPoint) { handleError(qBindExpectedInteger); return false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
305
306
                                           if (!neg && i<quint64( std::numeric_limits<qint64>::max())  ) { t =  qint64(i); cachedNumber=None; return true; }
                                           if ( neg && i<quint64(-std::numeric_limits<qint64>::max())-1) { t = -qint64(i); cachedNumber=None; return true; }
307
308
                                           handleError(qBindExpectedSmallerNumber); return false; }
    bool tryBind    (     QVariant& dst) { if (caching) { return caching->tryBind(dst) && cacheOut(); }
309
        auto suspended = setErrorHandler();
310

311
        quint32 size=0; QIdentifier key; QVariant item;
312
313
314
315
316
317
318
        if (       trySequence(&size)) { setErrorHandler(suspended); QVariantList l; while (tryItem(   )) { l.append(              tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); }
        if (       tryRecord  (&size)) { setErrorHandler(suspended); QVariantMap  l; while (tryItem(key)) { l.insert(key.latin1(), tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); }
        bool        b; if (tryBind(b)) { setErrorHandler(suspended); dst = QVariant(b                                       ); return true; }
        quint64     u; if (tryBind(u)) { setErrorHandler(suspended); dst = QVariant(u                                       ); return true; } // may fail after consuming integer part
        qint64      l; if (tryBind(l)) { setErrorHandler(suspended); dst = QVariant(l                                       ); return true; } // may fail after consuming integer part
        double      d; if (tryBind(d)) { setErrorHandler(suspended); dst = QVariant(d+/*integer part consumed by one of*/u+l); return true; }
        QUtf8Data   s; if (tryBind(s)) { setErrorHandler(suspended);
319
            QByteArray b;
320
321
322
323
324
325
            if (toByteArray(b, s)) {
                toVariant(dst, b);
                return true;
            }
            dst = QVariant(QString::fromUtf8(s.utf8()));
            return true;
326
        }
327
        if (tryNull()) { setErrorHandler(suspended); dst = QVariant::fromValue(nullptr); return true; }
328
        setErrorHandler(suspended);
329
330
        if (tryAny() || handleError(qBindUnexpectedValue)) { dst = QVariant(); return true; }
        return false;
331
    }
332

333
    bool tryOut (              ) { if (caching) { bool out = caching->tryOut(); if (out) { cacheLevel--; cacheOut(); } return out; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
334
                                   auto level = levels.pop();
335
                                   while (get(',', "}]")) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
336
337
                                       tryAny();
                                   }
338
                                   return get(*level.end, "}]"); }
339
    bool tryItem(QIdentifierLiteral u) { if (caching) { return caching->tryItem(u); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
                                   QIdentifier s;
                                   while (true) {
                                       if (levels.last().cachedItems.          contains(QIdentifier(u))) { // must be checked before we consume tryItem(s)
                                           cachedValue = levels.last().cachedItems.take(QIdentifier(u));
                                           cacheReader.reset(&cachedValue);
                                           Q_ASSERT(!cacheLevel);
                                           caching = &cacheReader; // let outer QValueStatus drive QJsonReader depending on bound T
                                           return true;
                                       }
                                       else if (!tryItem(s)) { // record() end reached
                                           return false;
                                       }
                                       else if (u  ==  s ) {
                                           return true ;
                                       }
                                       else { // read cachedValue using a dedicated QValueStatus and QTransmogrifier<QValue> accepting value of any 'shape' (outer QTransmogrifier is specialized on outer T which is not usually generic enough)
                                           cachedValue = QJsonValue();
                                           cacheWriter.reset(&cachedValue);
                                           if (!QValueStatus(this).value().bind(QValueStatus(&cacheWriter).value())) return false;
                                           levels.last().cachedItems.insert(s, cachedValue);
                                           continue; // until !tryItem(s) or u==s
                                       }
                                   }}
363
    bool tryItem(QIdentifier& k) { if (caching) { return caching->tryItem(k); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
364
365
366
367
368
369
370
371
372
373
374
                                   Step& level = levels.last();
                                   if ((-1 < level.index && !get(',',level.end)) || next("[{\"ntf-0123456789.",'}')=='}') {
                                       return false;
                                   }
                                   level.index++;
                                   QUtf8Data s;
                                   if (!tryBind(s)) {
                                       return false;
                                   }
                                   k = QIdentifier(s.utf8()); // TODO check errors
                                   return get(':',level.end); }
375
    bool tryItem(              ) { if (caching) { return caching->tryItem(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
376
377
378
379
380
381
                                   Step& level = levels.last();
                                   if ((-1 < level.index && !get(',',level.end)) || next("[{\"ntf-0123456789.",']')==']') {
                                       return false;
                                   }
                                   level.index++;
                                   return true; }
382

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
383
    bool isValid() const noexcept { return io; }
384
    bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("(%1:%2)").arg(line).arg(column).append(context)) : false; }
385
private:
386
387
388
    CachedNumber getNumber() {
        if (cachedNumber!=None) return cachedNumber;

389
        neg                         = get('-', "[{\"ntf-0123456789.");
390
        qint8 digit;
391
        if ((digit                  = getDigit()) < 0) {
392
            handleError(qBindExpectedDecimal);
393
            return None; // do not accept no digit otherwise we may accept an empty mantissa or string!
394
        }
395
        cachedNumber=Integer;
396
        i=0; d=0;
397
        do {// TODO return FloatingPoint on overflow (precision being limited by the type)
398
            i=i*10+quint8(digit); d=d*10+digit;
399
400
401
        }
        while (0 <= (digit          = getDigit())); // accept many leading '0' which is more permissive than JSON

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
402
        if (                          get('.')) { // TODO handle as negative exponent to support scientific notation of integers
403
            cachedNumber=FloatingPoint;
404
405
406
407
408
409
410
411
412
413
414
            // roughly
            // d = i;
            // m_stream >> decimal // except it would eat exponent
            // d += decimal
            double decimal=.1;
            while (0 <= (digit      = getDigit())) {
                d+=decimal*digit; decimal/=10;
            }
        }

        if (                          get('e') || get('E')) {
415
            qint64 exponent = 0;
416
417
418
            bool isNegativeExponent = get('-');
                                      get('+');
            while (0 <= (digit      = getDigit())) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
419
                exponent=exponent*10+digit; // TODO return None on overflow
420
            }
421
            // TODO return FloatingPoint on overflow (precision being limited by the type)
422
423
424
425
            d *=10^(isNegativeExponent ? -exponent : exponent);
        }
        // TODO if (!next(",]}") {}

426
        if (neg)
427
428
            d=-d;

429
        return cachedNumber;
430
    }
431
    qint8 getDigit(quint8 base = 10) { Q_ASSERT(0<base);
432
        qint8 digit;
433
434
435
436
437
        if ( 0 <= (digit=nextChar()-'0'   ) && digit < base && base <= 10) { getChar(); return digit; }
        if (10 <= (digit=nextChar()-'a'+10) && digit < base && base <= 36) { getChar(); return digit; }
        if (10 <= (digit=nextChar()-'A'+10) && digit < base && base <= 36) { getChar(); return digit; }
        return -1;
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
438
    char getCharInString() // TODO Support user-defined QTextCodec
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
    {
        if (nextChar() == '\0' || nextChar() == '"') {
            return '\0';
        }
        if (get('\\')) {
            unsigned hex = 0;
            switch (getChar())
            {
            case'\\': return '\\';
            case '"': return  '"';
            case 'b': return '\b';
            case 'f': return '\f';
            case 'n': return '\n';
            case 'r': return '\r';
            case 't': return '\t';
            case '/': return  '/';
            case 'u': {
                int digits = 0, digit;
                while ((digits++ < 4) && (0 <= (digit=getDigit(16)))) {
                    hex = 16*hex + unsigned(digit);
                }
                if (digits==4) {
                    return char(hex);
                }
                return  '?';
            }
            default : return  '?';
            }
        }
        else {
            return getChar();
        }
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
472
473
474
475
476
477
478
    bool nextIs(char expected, const char* validChars = nullptr) { return next(validChars, expected) == expected; }
    bool get(char expected, const char* validChars = nullptr) {
        if (nextIs(expected, validChars)) {
            return getChar() == expected;
        }
        return false;
    }
479
    char getChar () { char c; if (io->getChar(&c)     ) { if (c=='\n') { line++; column=0; } else { column++; } index++; return c; } return '\0'; }
480
481
    char nextChar() { char c; if (io->peek   (&c,1)==1) {
            return c; } return '\0'; }
482
483
484
485
486
    char next(const char* validChars, char expected='\0') {
        if (validChars) {
            while (!(nextChar() == expected || strchr(validChars, nextChar()) || nextChar() == '\0')) {
                char ignored = getChar();
                if (!isspace(ignored)) {
487
488
489
                    if (!handleError(qBindIgnoredCharacter)) {
                        return false;
                    }
490
491
492
493
494
495
496
497
498
                }
            }
        }
        return nextChar();
    }

    QIODevice* io;
    int line = 0, column = 0, index = -1;
    QStack<Step> levels = QStack<Step>(); //!< dynamic context required to implement item() and report meaningful errors
499

500
501
502
503
    // Read caching of numbers
    CachedNumber cachedNumber = None;
    double d; quint64 i; bool neg;

504
505
506
    bool cacheOut() { if (!cacheLevel) {
                          caching = nullptr;
                      }
507
508
                      return true; }

509
    // Read/Write caching of out-of-order item keys
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
510
511
512
    quint8       cacheLevel    = 0;
    QJsonValue   cachedValue      ; //!< Only used AFTER --cacheLevel==1
    QJsonBuilder cacheWriter      ;
513
    QAbstractValue* caching = nullptr; //!< Only used when cacheLevel>=1 && key!=expectedKey
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
514
    QJsonVisitor cacheReader      ; //!< Only used as caching
515
516
517
};

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
518
// QJson* support
519
520
521
522
523

#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>

524
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
525
struct QTransmogrifier<QJsonValue> {
526
    static QValueEnd zap(QValue&& v, QJsonValue&& j) {
527
        if (v->mode()==Write) {
528
529
530
531
532
            if (j.isObject()) return v.bind(j.toObject());
            if (j.isArray ()) return v.bind(j.toArray ());
            if (j.isBool  ()) return v.bind(j.toBool  ());
            if (j.isDouble()) return v.bind(j.toDouble());
            if (j.isString()) return v.bind(j.toString());
533
534
            if (j.isNull  ()) return v.null();
            if (j.isUndefined() || v->handleError(qBindUnexpectedValue)) return v.any();
535
            return QValueStatus();
536
        }
537
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
538
    }
539
    static QValueEnd zap(QValue&& v, QJsonValue& j) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
540
        if (v->mode()==Write) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
541
            return zap(std::move(v),std::move(j));
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
542
        }
543
        else if (v->mode()==Read) {
544
            QValueStatus r;
545
            auto suspended = v->setErrorHandler();
546
547
548
549
550
551
552
553
            QJsonArray  a; if ((r = v.bind(a))) { j =                   a  ; v->setErrorHandler(suspended); return r; }
            QJsonObject o; if ((r = v.bind(o))) { j =                   o  ; v->setErrorHandler(suspended); return r; }
            QString     t; if ((r = v.bind(t))) { j = QJsonValue(       t) ; v->setErrorHandler(suspended); return r; }
            bool        b; if ((r = v.bind(b))) { j = QJsonValue(       b) ; v->setErrorHandler(suspended); return r; }
            qint64      i; if ((r = v.bind(i))) { j = QJsonValue(double(i)); v->setErrorHandler(suspended); return r; }
            quint64     u; if ((r = v.bind(u))) { j = QJsonValue(double(u)); v->setErrorHandler(suspended); return r; }
            double      d; if ((r = v.bind(d))) { j = QJsonValue(       d ); v->setErrorHandler(suspended); return r; }
            /**/           if ((r = v.null( ))) { j = QJsonValue(         ); v->setErrorHandler(suspended); return r; }
554
            v->setErrorHandler(suspended);
555
            if (v->handleError(qBindUnexpectedValue)) { j = QJsonValue(QJsonValue::Undefined); }
556
            return r;
557
        }
558
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
559
560
561
    }
};

562
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
563
struct QTransmogrifier<QJsonArray> {
564
    static QValueEnd zap(QValue&& v, QJsonArray&& j) {
565
        if (v->mode()==Write) {
566
567
            quint32 size=quint32(j.size());
            auto s(v.sequence(&size));
568
            for (QJsonValue item : j) {
569
570
571
572
                s = s.bind(item);
            }
            return s;
        }
573
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
574
    }
575
    static QValueEnd zap(QValue&& v, QJsonArray& j) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
576
        if (v->mode()==Write) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
577
            return zap(std::move(v),std::move(j));
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
578
        }
579
        else if (v->mode()==Read) {
580
            auto s(v.sequence());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
581
            QVal<QSequence> i;
582
            while ((i = s.item())) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
583
584
585
                QJsonValue json;
                if ((s = i.bind(json)))
                    j.append(json);
586
587
588
            }
            return s;
        }
589
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
590
591
592
    }
};

593
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
594
struct QTransmogrifier<QJsonObject> {
595
    static QValueEnd zap(QValue&& v, QJsonObject&& j) {
596
        if (v->mode()==Write) {
597
598
599
            quint32 size=quint32(j.size());
            auto s(v.record(&size));
            for (QString key : j.keys()) {
600
601
                QIdentifier n(key.toLatin1());
                s = s.item(n).bind(QJsonValue(j[key]));
602
603
604
            }
            return s;
        }
605
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
606
    }
607
    static QValueEnd zap(QValue&& v, QJsonObject& j) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
608
        if (v->mode()==Write) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
609
            return zap(std::move(v),std::move(j));
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
610
        }
611
        else if (v->mode()==Read) {
612
            auto s(v.record());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
613
            QIdentifier k; QVal<QRecord> i;
614
            while ((i = s.item(k))) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
615
616
                QJsonValue json;
                if ((s = i.bind(json)))
617
                    j.insert(k.latin1(),json);
618
619
620
            }
            return s;
        }
621
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
622
623
    }
};