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/

QVariant_impl.h 12 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
/****************************************************************************
 * **
 * ** 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 <QtCore/qvariant.h>
#include <QtCore/qstack.h>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
45
#include "QValue.h"
46
47

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
48
// QVariant* support for the fixed set of QVariantBuilder's BindNative types
49

50
class QVariantBuilder : public QAbstractValueWriter
51
52
53
54
{
    Q_DISABLE_COPY(QVariantBuilder)
public:
    QVariantBuilder(QVariant* v) : variant(v) { Q_ASSERT(v); }
55
   ~QVariantBuilder() { while (!levels.isEmpty()) tryOut(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
56
    void reset(QVariant* v) { variant=v; Q_ASSERT(v); levels.resize(0); }
57
58

    // Shortcuts
59
60
61
    /**/                 QValue    value   (                  ) { return QCur(this).value(); }
    /**/                 QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
    template<typename T> QValueEnd bind    (             T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
62
63
64
protected:
    // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype

65
66
    bool tryBind(QUtf8DataView u) {
        return tryBind(QUtf8Data(u));
67
    }
68
    bool tryBind(  QStringView u) { return tryBind(u.toString()); }
69

70
    bool tryBind(  QUtf8Data&& t) {
71
72
73
        set(QVariant::fromValue(t));
        return true;
    }
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    bool tryBind(    QString&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(       bool&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(      qint8&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(     quint8&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(     qint16&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(    quint16&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(     qint32&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(    quint32&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(     qint64&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(    quint64&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(      float&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(     double&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind( QByteArray&& t) { set(QVariant::fromValue(t)); return true; }
    bool tryBind(   QVariant&& v) { set(                    v ); return true; }

89
90
    bool tryAny () { set(QVariant           (       )); return true; }
    bool tryNull() { set(QVariant::fromValue(nullptr)); return true; }
91
92
93
94
95
96
97

    bool trySequence(quint32* =nullptr) { levels.push(Level(                 )); return true; }
    bool tryRecord  (quint32* =nullptr) { levels.push(Level(qBindExpectedItem)); return true; }

    bool tryItem(QIdentifier& n) { levels.last().key=n            ; return true; }
    bool tryItem(              ) { levels.last().key=QIdentifier(); return true; }
    bool tryOut (              ) { auto level = levels.pop(); set(!level.key.isNull() ? QVariant(level.object) : QVariant(level.array)); return true; }
98
99
100
101
102
103
104
private:
    void set(const QVariant& v) {
        if (levels.isEmpty()) {
            *variant = v;
        }
        else {
            if (!levels.last().key.isNull())
105
                levels.last().object[levels.last().key.latin1()]=v;
106
107
108
109
110
111
            else
                levels.last().array.append(v);
        }
    }

    QVariant* variant;
112
    struct Level { QIdentifier key; /* TODO union */ QVariantMap object; QVariantList array; Level(QIdentifier k=QIdentifier()) : key(k) {} };
113
114
115
116
117
    QStack<Level> levels = QStack<Level>(); //!< minimal dynamic context to implement out() and ensure actual building in case QVariantBuilder is abandoned
};

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

118
class QVariantVisitor : public QAbstractValueReader
119
120
121
122
123
{
    Q_DISABLE_COPY(QVariantVisitor)
public:
    Q_ENABLE_MOVE_DEFAULT(QVariantVisitor)
    QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); }
124
    void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); }
125

126
    QAsciiData currentPath() {
127
        QByteArray path;
128
        Q_FOREACH(Level l, levels) {
129
130
            if (l.key.isNull()) { path.append('{').append(                   l.key.utf8() ); }
            else                { path.append('[').append(QByteArray::number(l.idx       )); }
131
        }
132
        return QAsciiData(path);
133
134
135
136
137
    }
protected:
    // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype

    template<typename T>
138
    bool tryBind(T& t) { if (current()->type()==qMetaTypeId<T>()) { t = current()->value<T>(); return true; } handleError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; }
139

140
    bool tryBind( QUtf8Data& t) { if (current()->userType() // TODO ->type() if QUtf8Data lands into Qt
141
142
143
144
                                                       ==qMetaTypeId< QUtf8Data>()) { t = current()->value< QUtf8Data>(); return true; } handleError(qBindExpectedText           ); return false; }
    bool tryBind(   QString& t) { if (current()->type()==qMetaTypeId<   QString>()) { t = current()->value<   QString>(); return true; } handleError(qBindExpectedText           ); return false; }
    bool tryBind(      bool& t) { if (current()->type()==qMetaTypeId<      bool>()) { t = current()->value<      bool>(); return true; } handleError(qBindExpectedBoolean        ); return false; }
    bool tryBind(QByteArray& t) { if (current()->type()==qMetaTypeId<QByteArray>()) { t = current()->value<QByteArray>(); return true; } handleError(qBindExpectedBytes          ); return false; }
145
    // Convert numerical types to strictly larger ones // TODO convert all compatible values
146
    bool tryBind(     qint8& t) { if (current()->type()==qMetaTypeId<     qint8>()) { t = current()->value<     qint8>(); return true; } handleError(qBindExpectedInteger        ); return false; }
147
    bool tryBind(    qint16& t) { if (current()->type()==qMetaTypeId<     qint8>()||
148
                                      current()->type()==qMetaTypeId<    qint16>()) { t = current()->value<    qint16>(); return true; } handleError(qBindExpectedInteger        ); return false; }
149
    bool tryBind(    qint32& t) { if (current()->type()==qMetaTypeId<     qint8>()||
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
150
                                      current()->type()==qMetaTypeId<    qint16>()||
151
                                      current()->type()==qMetaTypeId<    qint32>()) { t = current()->value<    qint32>(); return true; } handleError(qBindExpectedInteger        ); return false; }
152
    bool tryBind(    qint64& t) { if (current()->type()==qMetaTypeId<     qint8>()||
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
153
154
                                      current()->type()==qMetaTypeId<    qint16>()||
                                      current()->type()==qMetaTypeId<    qint32>()||
155
156
                                      current()->type()==qMetaTypeId<    qint64>()) { t = current()->value<    qint64>(); return true; } handleError(qBindExpectedInteger        ); return false; }
    bool tryBind(    quint8& t) { if (current()->type()==qMetaTypeId<    quint8>()) { t = current()->value<    quint8>(); return true; } handleError(qBindExpectedPositiveInteger); return false; }
157
    bool tryBind(   quint16& t) { if (current()->type()==qMetaTypeId<    quint8>()||
158
                                      current()->type()==qMetaTypeId<   quint16>()) { t = current()->value<   quint16>(); return true; } handleError(qBindExpectedPositiveInteger); return false; }
159
    bool tryBind(   quint32& t) { if (current()->type()==qMetaTypeId<    quint8>()||
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
160
                                      current()->type()==qMetaTypeId<   quint16>()||
161
                                      current()->type()==qMetaTypeId<   quint32>()) { t = current()->value<   quint32>(); return true; } handleError(qBindExpectedPositiveInteger); return false; }
162
    bool tryBind(   quint64& t) { if (current()->type()==qMetaTypeId<    quint8>()||
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
163
164
                                      current()->type()==qMetaTypeId<   quint16>()||
                                      current()->type()==qMetaTypeId<   quint32>()||
165
166
                                      current()->type()==qMetaTypeId<   quint64>()) { t = current()->value<   quint64>(); return true; } handleError(qBindExpectedPositiveInteger); return false; }
    bool tryBind(     float& t) { if (current()->type()==qMetaTypeId<     float>()) { t = current()->value<     float>(); return true; } handleError(qBindExpectedDecimal        ); return false; }
167
    bool tryBind(    double& t) { if (current()->type()==qMetaTypeId<     float>()||
168
                                      current()->type()==qMetaTypeId<    double>()) { t = current()->value<    double>(); return true; } handleError(qBindExpectedDecimal        ); return false; }
169

170
171
    bool trySequence(quint32* =nullptr) { if (current()->type()==QVariant::List  ) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; }
    bool tryRecord  (quint32* =nullptr) { if (current()->type()==QVariant::Map   ) { levels.push(Level()); return true; } handleError(qBindExpectedRecord  ); return false; }
172
173

    bool tryNull() { if ( current()->isNull ()) { return true; } handleError(qBindExpectedNull   ); return false; }
174

175
176
177
    bool tryItem(QIdentifier& k) { levels.last().key=k; return (levels.last().item = current(1)->toMap ().value(QString(levels.last().key.latin1()), QVariant())).isValid(); }
    bool tryItem(              ) { levels.last().idx++; return (levels.last().item = current(1)->toList().value(        levels.last().idx          , QVariant())).isValid(); }
    bool tryOut (              ) { levels.pop()       ; return true; }
178

179
    bool tryAny () { return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
180
    bool isValid() const noexcept { return value; }
181
    bool handleError(QIdentifierLiteral error, QString context = QString()) { return errorHandler ? errorHandler(error, QString(currentPath().latin1()).append(context)) : false; }
182
183
184
185
private:
    const QVariant* current(unsigned outer=0) const { return unsigned(levels.size())-outer <= 0 ? value : &(levels[unsigned(levels.size())-outer-1].item); }

    const QVariant* value;
186
    struct Level { QIdentifier key=QIdentifier(); int idx=-1; QVariant item; Level() = default; };
187
188
    QStack<Level> levels;
};