/**************************************************************************** * ** * ** 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 #include "QValue.h" // ////////////////////////////////////////////////////////////////////////// // QSettings* support for the fixed set of QSettingsWriter's BindNative types class QSettingsWriter : public QAbstractValueWriter { Q_DISABLE_COPY(QSettingsWriter) public: QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); } // 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: bool tryBind ( quint8&& t) { settings->setValue(key(), int(t) ); return true; } bool tryBind ( quint16&& t) { settings->setValue(key(), int(t) ); return true; } bool tryBind ( quint32&& t) { settings->setValue(key(), int(t) ); return true; } bool tryBind (QUtf8DataView u) { settings->setValue(key(), QString::fromUtf8(u.data(), u.size())); return true; } bool tryBind ( QStringView s) { settings->setValue(key(), s.toString() ); return true; } bool tryBind ( QString&& s) { settings->setValue(key(), s ); return true; } bool tryNull ( ) { settings->setValue(key(), QVariant() ); return true; } bool trySequence(quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level( )); return true; } bool tryRecord (quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level(qBindExpectedItem)); return true; } bool tryItem(QIdentifier& n) { levels.last().key=n; return true; } bool tryItem( ) { levels.last().idx++; return true; } bool tryOut ( ) { levels.pop(); settings->endGroup(); return true; } private: QString key() { Q_ASSERT(!levels.isEmpty()); return levels.size()==1 ? QString("") : !levels.last().key.isNull() ? QString (levels.last().key.latin1()) : QString::number(levels.last().idx ); } QSettings* settings; struct Level { QIdentifier key; quint32 idx=0; Level(QIdentifier k=QIdentifier()) : key(k) {} }; QStack levels = QStack(); //!< minimal dynamic context to implement out() and ensure actual building in case QSettingsWriter is abandoned }; // -------------------------------------------------------------------------- class QSettingsReader : public QAbstractValueReader { Q_DISABLE_COPY(QSettingsReader) public: Q_ENABLE_MOVE(QSettingsReader, std::swap(isChoice, o.isChoice); ) QSettingsReader(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); } 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); } // Shortcuts template QValueStatus bind(T&& t) { return QValueStatus(this).value().bind(std::forward(t)); } protected: template bool tryBind(T& t) { return set(t, "Expected declared metatype T"); } bool tryBind(QUtf8Data& t) { return set(t, qBindExpectedText ); } bool tryBind( QString& t) { return set(t, qBindExpectedText ); } bool tryBind( bool& t) { return set(t, qBindExpectedBoolean); } bool tryBind( QByteArray& t) { return set(t, qBindExpectedBytes ); } // Convert numerical types to strictly larger ones // TODO convert all compatible values bool tryBind( qint8& t) { return set(t, qBindExpectedInteger ); } bool tryBind( qint16& t) { return set(t, qBindExpectedInteger ); } bool tryBind( qint32& t) { return set(t, qBindExpectedInteger ); } bool tryBind( qint64& t) { return set(t, qBindExpectedInteger ); } bool tryBind( quint8& t) { return set(t, qBindExpectedPositiveInteger); } bool tryBind( quint16& t) { return set(t, qBindExpectedPositiveInteger); } bool tryBind( quint32& t) { return set(t, qBindExpectedPositiveInteger); } bool tryBind( quint64& t) { return set(t, qBindExpectedPositiveInteger); } bool tryBind( float& t) { return set(t, qBindExpectedDecimal ); } bool tryBind( double& t) { return set(t, qBindExpectedDecimal ); } bool trySequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } reportError(qBindExpectedSequence); return false; } bool tryRecord (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(qBindExpectedItem)); return true; } reportError(qBindExpectedRecord ); return false; } bool tryNull ( ) { if (settings->value(key()).isNull()) { return true; } reportError(qBindExpectedNull ); return false; } bool tryItem(QIdentifier& k) { levels.last().key=k ; return true; } bool tryItem( ) { levels.last().idx++ ; return true; } bool tryOut ( ) { levels.pop(); settings->endGroup(); return true; } bool _isOk() const noexcept { return settings; } void reportError(QIdentifierLiteral error, QString context = QString()) { if (errorHandler) errorHandler(error, QString(currentPath().latin1()).append(context)); } private: template bool set(T& t, QIdentifierLiteral error) { QVariant v = settings->value(key()); if (v.convert(qMetaTypeId())) { t = v.value(); return true; } reportError(error); return false; } QString key() { Q_ASSERT(!levels.isEmpty()); return !levels.last().key.isNull() ? QString (levels.last().key.latin1()) : QString::number(levels.last().idx ); } QSettings* settings; struct Level { QIdentifier key; int idx=-1; bool isGroup=true; Level(QIdentifier n=QIdentifier(), bool isGroup=true) : key(n), isGroup(isGroup) {} }; QStack levels; bool isChoice = false; };