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

#include "QBind_impl.h"

// //////////////////////////////////////////////////////////////////////////
// QBind<QVariant*,T> support for the fixed set of QVariantBuilder's BindNative types

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
50
class QVariantBuilder : public IWriter
51
{
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
52
    Q_DISABLE_COPY(QVariantBuilder)
53
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
54
    QVariantBuilder(QVariant* v) : variant(v) { Q_ASSERT(v); }
55
56
   ~QVariantBuilder() { while (!levels.isEmpty()) _out(); }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
57
    // Shortcuts
58
59
60
    /**/             Val<Cur<>> value   (                  ) { return Cur<>(this).value(); }
    /**/             Seq<Cur<>> sequence(quint32* s=nullptr) { return Cur<>(this).value().sequence            ( s); }
    template<typename T> Cur<>  bind    (             T&& t) { return Cur<>(this).value().bind(std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
61
protected:
62
    template<typename T>
63
    bool _bind    (                T&& t) { set(QVariant::fromValue(t)); return true; }
64
65
    bool _bind    (        const char* t) { set(QVariant           (t)); return true; }
    bool _null    (                     ) { set(QVariant           ( )); return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
66

67
68
69
    bool _sequence(quint32* rows=nullptr) { Q_UNUSED(rows); levels.push(Step(nullptr)); return true; }
    bool _record  (quint32* cols=nullptr) { Q_UNUSED(cols); levels.push(Step(""     )); return true; }

70
71
72
    bool _item(QName n) { levels.last().key=n      ; return true; }
    bool _item(       ) { levels.last().key=nullptr; return true; }
    bool _out (       ) { auto level = levels.pop(); set(!level.key.isNull() ? QVariant(level.object) : QVariant(level.array)); return true; }
73
74
75
private:
    void set(const QVariant& v) {
        if (levels.isEmpty()) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
76
            *variant = v;
77
78
79
80
81
82
83
84
85
        }
        else {
            if (!levels.last().key.isNull())
                levels.last().object[levels.last().key]=v;
            else
                levels.last().array.append(v);
        }
    }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
86
    QVariant* variant;
87
    struct Step { QUtf8String key; /* TODO union */ QVariantMap object; QVariantList array; Step(const char* k=nullptr) : key(k) {} };
88
89
90
91
92
    QStack<Step> levels = QStack<Step>(); //!< minimal dynamic context to implement out() and ensure actual building in case QVariantBuilder is abandoned
};

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

93
class QVariantVisitor : public IReader
94
{
95
    Q_DISABLE_COPY(QVariantVisitor)
96
97
public:
    Q_ENABLE_MOVE_DEFAULT(QVariantVisitor)
98
    QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); }
99

100
    struct Error { const char* error; QUtf8String path; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8String(error)+' '+path); } };
101
102
    QVector<Error> errors;

103
104
    QUtf8String currentPath() {
        QUtf8String path;
105
        Q_FOREACH(Step s, steps) {
106
107
            if (s.key) { path.append('{').append(s.key); }
            else       { path.append('[').append(QUtf8String::number(s.idx)); }
108
109
110
111
112
        }
        return path;
    }
protected:
    template<typename T>
113
114
115
116
    bool _bind    (                 T& t) {                 if (current()->type()==qMetaTypeId<T>()) { t = current()->value<T>(); return true; } _reportError("Expected T"         ); return false; }
    bool _sequence(quint32* rows=nullptr) { Q_UNUSED(rows); if (current()->type()==QVariant::List  ) {        steps.push(Step()); return true; } _reportError(qBindExpectedSequence); return false; }
    bool _record  (quint32* cols=nullptr) { Q_UNUSED(cols); if (current()->type()==QVariant::Map   ) {        steps.push(Step()); return true; } _reportError(qBindExpectedRecord  ); return false; }
    bool _null    (                     ) {                 if (current()->isNull()                ) {                            return true; } _reportError(qBindExpectedNull    ); return false; }
117

118
119
120
    bool _item(QUtf8String& k) { steps.last().key=k; return (steps.last().item = current(1)->toMap ().value(QString::fromUtf8(steps.last().key), QVariant())).isValid(); }
    bool _item(              ) { steps.last().idx++; return (steps.last().item = current(1)->toList().value(                  steps.last().idx , QVariant())).isValid(); }
    bool _out (              ) { steps.pop()       ; return true; }
121
122
123
124

    bool _isOk() { return value; }
    void _setChoice(bool v) { isChoice=v; }
    void _reportError(const char* error) { if (!isChoice) errors.append(Error{ error, currentPath() }); }
125
126
127
128
129
130
131
132
private:
    const QVariant* current(unsigned outer=0) const { return unsigned(steps.size())-outer <= 0 ? value : &(steps[unsigned(steps.size())-outer-1].item); }

    const QVariant* value;
    struct Step { const char* key=nullptr; int idx=-1; QVariant item; Step() = default; };
    QStack<Step> steps = QStack<Step>();
    bool isChoice = false;
};
133
// TODO BindNative other QVariant-supported types?
134
135
136
137
138

//// //////////////////////////////////////////////////////////////////////////
//// QBind<TResult,QVariant&> example support for the fixed set of QVariantBuilder's BindNative types + other convenient types

template<class TResult>
139
struct QBind<TResult, QVariant> {
140
141
142
143
    static TResult bind(Val<TResult>&& v, QVariant&& src) {
        if (v->mode()==Write) {
            if (src.type()==QVariant::List     ) return v.bind(src.value<QVariantList>()); // TODO  QSequentialIterable ?
            if (src.type()==QVariant::Map      ) return v.bind(src.value<QVariantMap >()); // TODO QAssociativeIterable ?
144
145
            // TODO QVariant::Hash

146
147
148
149
            if (src.type()==QVariant::Bool     ) return v.bind(src.value<        bool>());
            if (src.type()==QVariant::ByteArray) return v.bind(src.value<  QByteArray>());
            if (src.type()==QVariant::String   ) return v.bind(src.value<     QString>());
            if (src.type()==QVariant::Char     ) return v.bind(src.toString());
150

151
            if (src.type()==QVariant::ULongLong) return v.bind(src.value<  quint64>());
152
            if (src.type()==QVariant::UInt     ) return v.bind(src.value<unsigned int>());
153
            if (src.type()==QVariant::LongLong ) return v.bind(src.value<   qint64>());
154
155
            if (src.type()==QVariant::Int      ) return v.bind(src.value<         int>());
            if (src.type()==QVariant::Double   ) return v.bind(src.value<      double>());
156
157
158
159

            // TODO convert builtin types which canConvert<QString>
            // \sa QBind<TSrcResult, Val<TDst>&&, IsReader<TSrcResult>>

160
            return v.null();
161
        }
162
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
163
    }
164
165
166
    static TResult bind(Val<TResult>&& v, QVariant& dst) {
        if (v->mode()==Write) {
            return bind(std::move(v),std::move(dst));
167
        }
168
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
169
170
    }
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
171

172
//template<class TResult>
173
//struct QBind<TResult, QVariant&, IsReader<TResult>> { static TResult bind(Val<TResult>&& v, QVariant& dst) {
174
175
//    TResult r;
//    {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
176
//        QScopedChoice<Val<TResult>> choice(src);
177
178
179
//        QVariantList a; if ((r = src.bind(a))) { dst =          a ; return r; } // NB We cannot try QStringList before QVariantList as it will always work with our logical model...
//        QVariantMap  o; if ((r = src.bind(o))) { dst =          o ; return r; }
//        bool         b; if ((r = src.bind(b))) { dst = QVariant(b); return r; }
180
181
//        quint64   u; if ((r = src.bind(u))) { dst = QVariant(u); return r; }
//        qint64    l; if ((r = src.bind(l))) { dst = QVariant(l); return r; }
182
183
184
185
186
187
188
//        double       d; if ((r = src.bind(d))) { dst = QVariant(d); return r; }
//        QByteArray   x; if ((r = src.bind(x))) { dst = QVariant(x); return r; }
//        QString      t; if ((r = src.bind(t))) { dst = QVariant(t); return r; }
//    }
//    if (!(r = src.null())) r.reportError("Expected boolean|decimal|text|sequence|record|null");
//    /**/                                         dst = QVariant( ); return r;
//}};