/**************************************************************************** * ** * ** 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 "QValue.h" // ////////////////////////////////////////////////////////////////////////// // QVariant* support for the fixed set of QVariantBuilder's BindNative types class QVariantBuilder : public QAbstractValueWriter { Q_DISABLE_COPY(QVariantBuilder) public: QVariantBuilder(QVariant* v) : variant(v) { Q_ASSERT(v); } ~QVariantBuilder() { while (!levels.isEmpty()) tryOut(); } void reset(QVariant* v) { variant=v; Q_ASSERT(v); levels.resize(0); } // Shortcuts /**/ QValue value ( ) { return QValueStatus(this).value(); } /**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); } template QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward(t)); } protected: // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype bool tryBind(QUtf8DataView u) { return tryBind(QUtf8Data(u)); } bool tryBind( QStringView u) { return tryBind(u.toString()); } bool tryBind( QUtf8Data&& t) { set(QVariant::fromValue(t)); return true; } 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; } bool tryNull( ) { set(QVariant ( )); return true; } 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; } private: void set(const QVariant& v) { if (levels.isEmpty()) { *variant = v; } else { if (!levels.last().key.isNull()) levels.last().object[levels.last().key.latin1()]=v; else levels.last().array.append(v); } } QVariant* variant; struct Level { QIdentifier key; /* TODO union */ QVariantMap object; QVariantList array; Level(QIdentifier k=QIdentifier()) : key(k) {} }; QStack levels = QStack(); //!< minimal dynamic context to implement out() and ensure actual building in case QVariantBuilder is abandoned }; // -------------------------------------------------------------------------- class QVariantVisitor : public QAbstractValueReader { Q_DISABLE_COPY(QVariantVisitor) public: Q_ENABLE_MOVE_DEFAULT(QVariantVisitor) QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); } void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); errors.resize(0); } struct Error { QIdentifierLiteral error; QAsciiData path; template T bind(QVal&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } }; QVector errors; QAsciiData currentPath() { QByteArray path; Q_FOREACH(Level l, levels) { if (l.key.isNull()) { path.append('{').append( l.key.utf8() ); } else { path.append('[').append(QByteArray::number(l.idx )); } } return QAsciiData(path); } protected: // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype template bool tryBind(T& t) { if (current()->type()==qMetaTypeId()) { t = current()->value(); return true; } _reportError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; } bool tryBind( 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 tryBind( QString& t) { if (current()->type()==qMetaTypeId< QString>()) { t = current()->value< QString>(); return true; } _reportError(qBindExpectedText ); return false; } bool tryBind( bool& t) { if (current()->type()==qMetaTypeId< bool>()) { t = current()->value< bool>(); return true; } _reportError(qBindExpectedBoolean ); return false; } bool tryBind(QByteArray& t) { if (current()->type()==qMetaTypeId()) { t = current()->value(); return true; } _reportError(qBindExpectedBytes ); return false; } // Convert numerical types to strictly larger ones // TODO convert all compatible values bool tryBind( qint8& t) { if (current()->type()==qMetaTypeId< qint8>()) { t = current()->value< qint8>(); return true; } _reportError(qBindExpectedInteger ); return false; } bool tryBind( qint16& t) { if (current()->type()==qMetaTypeId< qint8>()|| current()->type()==qMetaTypeId< qint16>()) { t = current()->value< qint16>(); return true; } _reportError(qBindExpectedInteger ); return false; } bool tryBind( 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 tryBind( 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 tryBind( quint8& t) { if (current()->type()==qMetaTypeId< quint8>()) { t = current()->value< quint8>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } bool tryBind( quint16& t) { if (current()->type()==qMetaTypeId< quint8>()|| current()->type()==qMetaTypeId< quint16>()) { t = current()->value< quint16>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } bool tryBind( quint32& t) { if (current()->type()==qMetaTypeId< quint8>()|| current()->type()==qMetaTypeId< quint16>()|| current()->type()==qMetaTypeId< quint32>()) { t = current()->value< quint32>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } bool tryBind( 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(qBindExpectedPositiveInteger); return false; } bool tryBind( float& t) { if (current()->type()==qMetaTypeId< float>()) { t = current()->value< float>(); return true; } _reportError(qBindExpectedDecimal ); return false; } bool tryBind( double& t) { if (current()->type()==qMetaTypeId< float>()|| current()->type()==qMetaTypeId< double>()) { t = current()->value< double>(); return true; } _reportError(qBindExpectedDecimal ); return false; } bool trySequence(quint32* =nullptr) { if (current()->type()==QVariant::List ) { levels.push(Level()); return true; } _reportError(qBindExpectedSequence); return false; } bool tryRecord (quint32* =nullptr) { if (current()->type()==QVariant::Map ) { levels.push(Level()); return true; } _reportError(qBindExpectedRecord ); return false; } bool tryNull ( ) { if (current()->isNull() ) { return true; } _reportError(qBindExpectedNull ); return false; } 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; } bool _isOk() const noexcept { return value; } void _setChoice(bool v) { isChoice=v; } void _reportError(QIdentifierLiteral error) { if (!isChoice) errors.append(Error{ error, currentPath() }); } 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; struct Level { QIdentifier key=QIdentifier(); int idx=-1; QVariant item; Level() = default; }; QStack levels; bool isChoice = false; };