QVariant_impl.h 12.1 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>

45
#include "QBind.h"
46 47 48 49 50 51 52 53 54 55

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

class QVariantBuilder : public IWriter
{
    Q_DISABLE_COPY(QVariantBuilder)
public:
    QVariantBuilder(QVariant* v) : variant(v) { Q_ASSERT(v); }
   ~QVariantBuilder() { while (!levels.isEmpty()) _out(); }
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 59 60 61 62 63 64

    // Shortcuts
    /**/             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)); }
protected:
    // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype

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

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

    bool _null(               ) { set(QVariant           ( )); return true; }

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

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

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

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

class QVariantVisitor : public IReader
{
    Q_DISABLE_COPY(QVariantVisitor)
public:
    Q_ENABLE_MOVE_DEFAULT(QVariantVisitor)
    QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
123
    void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); errors.resize(0); }
124

125
    struct Error { QIdentifierLiteral error; QAsciiData path; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } };
126 127
    QVector<Error> errors;

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

    template<typename T>
140
    bool _bind(T& t) { if (current()->type()==qMetaTypeId<T>()) { t = current()->value<T>(); return true; } _reportError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; }
141

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
172 173 174
    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; }
175

176 177 178
    bool _item(QIdentifier& k) { levels.last().key=k; return (levels.last().item = current(1)->toMap ().value(QString(levels.last().key.latin1()), 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; }
179

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
180
    bool _isOk() const noexcept { return value; }
181
    void _setChoice(bool v) { isChoice=v; }
182
    void _reportError(QIdentifierLiteral error) { if (!isChoice) errors.append(Error{ error, currentPath() }); }
183 184 185 186
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;
187
    struct Level { QIdentifier key=QIdentifier(); int idx=-1; QVariant item; Level() = default; };
188 189 190
    QStack<Level> levels;
    bool isChoice = false;
};