/**************************************************************************** * ** * ** Copyright (C) 2017 MinMaxMedical. * ** All rights reserved. * ** Contact: MinMaxMedical * ** * ** 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 #include #include "QBind_impl.h" // ////////////////////////////////////////////////////////////////////////// // QBind 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(); } // Shortcuts /**/ Val value ( ) { return QWriter(this).value ( ); } /**/ Val meta ( MetaData&& m) { return QWriter(this).meta ( m); } /**/ Val meta ( MetaData& m) { return QWriter(this).meta ( m); } /**/ Seq sequence(quint32* s=nullptr) { return QWriter(this).sequence ( s); } /**/ Rec record (quint32* s=nullptr) { return QWriter(this).record ( s); } /**/ QWriter null ( ) { return QWriter(this).null ( ); } template QWriter bind ( T&& t) { return QWriter(this).bind(std::forward(t)); } protected: template bool _bind ( T& t) { set(QVariant::fromValue(t)); return true; } bool _bind ( const char* t) { set(QVariant (t)); return true; } bool _null ( ) { set(QVariant ( )); return true; } 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; } bool _item(QByteArray& k) { levels.last().key=k ; 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; } private: void set(const QVariant& v) { if (levels.isEmpty()) { *variant = v; } else { if (!levels.last().key.isNull()) levels.last().object[levels.last().key]=v; else levels.last().array.append(v); } } QVariant* variant; struct Step { QByteArray key; /* TODO union */ QVariantMap object; QVariantList array; Step(const char* k=nullptr) : key(k) {} }; QStack levels = QStack(); //!< minimal dynamic context to implement out() and ensure actual building in case QVariantBuilder is abandoned }; // -------------------------------------------------------------------------- class VariantVisitorImpl; class QVariantVisitor : public QBaseReader { Q_PROTECTED_COPY(QVariantVisitor) public: Q_ENABLE_MOVE_DEFAULT(QVariantVisitor) QVariantVisitor(const QVariant* v); private: friend class QBaseReader; friend class VariantVisitorImpl; // uses method below QVariantVisitor(VariantVisitorImpl* outer) : QBaseReader(outer, false) {} }; class VariantVisitorImpl { Q_DISABLE_COPY(VariantVisitorImpl) public: VariantVisitorImpl(const QVariant* v) : value(v) { Q_ASSERT(v); } struct Error { const char* error; QByteArray path; template T bind(Val value) { return value.bind(QByteArray(error)+' '+path); } }; QVector errors; QByteArray currentPath() { QByteArray path; Q_FOREACH(Step s, steps) { if (s.key) { path.append('{').append(s.key); } else { char index[std::numeric_limits::digits10+1]; itoa(s.idx, index, 10); path.append('[').append(index); } } return path; } protected: friend class QScopedChoice>; void setChoice(bool v) { isChoice=v; } friend class QBaseReader; void reportError(const char* error) { if (!isChoice) errors.append(Error{ error, currentPath() }); } template friend class Val; // calls methods below template bool _bind ( T& t) { if (current()->type()==qMetaTypeId() ) { t = current()->value(); 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; } template friend class Seq; // calls methods below template friend class Rec; // calls methods below bool _item(QByteArray& 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; } 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 steps = QStack(); bool isChoice = false; }; QVariantVisitor::QVariantVisitor(const QVariant* v) : QBaseReader(new VariantVisitorImpl(v), true) {} template<> struct BindSupport : BindNative {}; template<> struct BindSupport : BindNative {}; template<> struct BindSupport : BindNative {}; template<> struct BindSupport : BindNative {}; template<> struct BindSupport : BindNative {}; template<> struct BindSupport : BindNative {}; // also handles const char* //// ////////////////////////////////////////////////////////////////////////// //// QBind example support for the fixed set of QVariantBuilder's BindNative types + other convenient types template struct QBind> { static TResult bind(Val dst, QVariant& src) { if (src.type()==QVariant::List ) return dst.bind(src.value()); // TODO QSequentialIterable ? if (src.type()==QVariant::Map ) return dst.bind(src.value()); // TODO QAssociativeIterable ? // TODO QVariant::Hash if (src.type()==QVariant::Bool ) return dst.bind(src.value< bool>()); if (src.type()==QVariant::ByteArray) return dst.bind(src.value< QByteArray>()); if (src.type()==QVariant::String ) return dst.bind(src.value< QString>()); if (src.type()==QVariant::Char ) return dst.bind(src.toString()); if (src.type()==QVariant::ULongLong) return dst.bind(src.value< qulonglong>()); if (src.type()==QVariant::UInt ) return dst.bind(src.value()); if (src.type()==QVariant::LongLong ) return dst.bind(src.value< qlonglong>()); if (src.type()==QVariant::Int ) return dst.bind(src.value< int>()); if (src.type()==QVariant::Double ) return dst.bind(src.value< double>()); // TODO convert builtin types which canConvert // \sa QBind&&, IsReader> return dst.null(); }}; //template //struct QBind> { static TResult bind(Val src, QVariant& dst) { // TResult r; // { // QScopedChoice> choice(src); // 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; } // qulonglong u; if ((r = src.bind(u))) { dst = QVariant(u); return r; } // qlonglong l; if ((r = src.bind(l))) { dst = QVariant(l); return r; } // 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; //}};