QVariant_impl.h 9.86 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
/****************************************************************************
 * **
 * ** 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"

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

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<Cursor> value   (                  ) { return Cursor(this).value(); }
    /**/             Seq<Cursor> sequence(quint32* s=nullptr) { return Cursor(this).value().sequence            ( s); }
    template<typename T> Cursor  bind    (             T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
61
protected:
62 63
    // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype

64
    template<typename T>
65
    bool _bind    (            T&& t) { set(QVariant::fromValue(t)); return true; }
66
    bool _bind    (    const char* u) { set(QVariant           (u)); return true; }
67
    bool _null    (                 ) { set(QVariant           ( )); return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
68

69 70
    bool _sequence(quint32* =nullptr) { levels.push(Level(nullptr)); return true; }
    bool _record  (quint32* =nullptr) { levels.push(Level(""     )); return true; }
71

72 73 74
    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; }
75 76 77
private:
    void set(const QVariant& v) {
        if (levels.isEmpty()) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
78
            *variant = v;
79 80 81 82 83 84 85 86 87
        }
        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
88
    QVariant* variant;
89 90
    struct Level { QUtf8String key; /* TODO union */ QVariantMap object; QVariantList array; Level(const char* k=nullptr) : key(k) {} };
    QStack<Level> levels = QStack<Level>(); //!< minimal dynamic context to implement out() and ensure actual building in case QVariantBuilder is abandoned
91 92 93 94
};

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

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

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

105 106
    QUtf8String currentPath() {
        QUtf8String path;
107 108 109
        Q_FOREACH(Level l, levels) {
            if (l.key) { path.append('{').append(l.key); }
            else       { path.append('[').append(QUtf8String::number(l.idx)); }
110 111 112 113
        }
        return path;
    }
protected:
114 115
    // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype

116
    template<typename T>
117 118 119 120
    bool _bind    (             T& t) { if (current()->type()==qMetaTypeId<T>()) { t = current()->value<T>(); return true; } _reportError("Expected T"         ); return false; }
    bool _sequence(quint32* =nullptr) { if (current()->type()==QVariant::List  ) {        levels.push(Level()); return true; } _reportError(qBindExpectedSequence); return false; }
    bool _record  (quint32* =nullptr) { if (current()->type()==QVariant::Map   ) {        levels.push(Level()); return true; } _reportError(qBindExpectedRecord  ); return false; }
    bool _null    (                 ) { if (current()->isNull()                ) {                            return true; } _reportError(qBindExpectedNull    ); return false; }
121

122 123 124
    bool _item(QUtf8String& k) { levels.last().key=k; return (levels.last().item = current(1)->toMap ().value(QString::fromUtf8(levels.last().key), QVariant())).isValid(); }
    bool _item(              ) { levels.last().idx++; return (levels.last().item = current(1)->toList().value(                  levels.last().idx , QVariant())).isValid(); }
    bool _out (              ) { levels.pop()       ; return true; }
125 126 127 128

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

    const QVariant* value;
133 134
    struct Level { const char* key=nullptr; int idx=-1; QVariant item; Level() = default; };
    QStack<Level> levels;
135 136
    bool isChoice = false;
};
137

138
// //////////////////////////////////////////////////////////////////////////
139
// QBind<QVariant> example support for the set of IBind's BindNative types + other convenient types
140

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
141
template<>
142
struct QBind<QVariant> {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
143
    static Cursor bind(Val<Cursor>&& v, QVariant&& src) {
144 145 146
        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 ?
147 148
            // TODO QVariant::Hash

149 150 151 152
            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());
153

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

160
            return v.null();
161
        }
162
        else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
163
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
164
    static Cursor bind(Val<Cursor>&& v, QVariant& dst) {
165 166
        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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
172
//template<>
173
//struct QBind<QVariant, Cursor, IsReader<Cursor>> { static Cursor bind(Val<Cursor>&& v, QVariant& dst) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
174
//    Cursor r;
175
//    {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
176
//        QScopedChoice<Val<Cursor>> 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;
//}};
189 190

// TODO convert builtin types which canConvert<QString>