QSettings_impl.h 8.51 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
/****************************************************************************
 * **
 * ** 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/qsettings.h>
#include <QtCore/qvariant.h>
#include <QtCore/qstack.h>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
46
#include "QValue.h"
47 48

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
49
// QSettings* support for the fixed set of QSettingsWriter's BindNative types
50

51
class QSettingsWriter : public QAbstractValueWriter
52 53 54
{
    Q_DISABLE_COPY(QSettingsWriter)
public:
55
    QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
56 57

    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
58
    /**/                 QValue        value   (                  ) { return QValueStatus(this).value(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
59
    /**/                 QSequence     sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
60
    template<typename T> QValueStatus  bind    (             T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
61
protected:
62 63 64 65 66 67 68 69 70 71 72 73 74 75
    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; }
76 77 78
private:
    QString key() {
        Q_ASSERT(!levels.isEmpty());
79 80 81 82
        return levels.size()==1 ? QString("") :
              !levels.last().key.isNull()
                 ? QString        (levels.last().key.latin1())
                 : QString::number(levels.last().idx         );
83 84 85
    }

    QSettings* settings;
86
    struct Level { QIdentifier key; quint32 idx=0; Level(QIdentifier k=QIdentifier()) : key(k) {} };
87 88 89 90 91
    QStack<Level> levels = QStack<Level>(); //!< minimal dynamic context to implement out() and ensure actual building in case QSettingsWriter is abandoned
};

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

92
class QSettingsReader : public QAbstractValueReader
93 94 95 96
{
    Q_DISABLE_COPY(QSettingsReader)
public:
    Q_ENABLE_MOVE(QSettingsReader, std::swap(isChoice, o.isChoice); )
97
    QSettingsReader(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
98

99
    QAsciiData currentPath() {
100
        QByteArray path;
101
        Q_FOREACH(Level l, levels) {
102 103
            if (l.key.isNull()) { path.append('/').append(                   l.key.utf8() ); }
            else                { path.append('/').append(QByteArray::number(l.idx       )); }
104
        }
105
        return QAsciiData(path);
106 107 108
    }

    // Shortcuts
109
    template<typename T> QValueStatus bind(T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
110 111
protected:
    template<typename T>
112
    bool tryBind(T& t) { return set(t, "Expected declared metatype T"); }
113

114 115 116 117
    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  ); }
118
    // Convert numerical types to strictly larger ones // TODO convert all compatible values
119 120 121 122 123 124 125 126 127 128
    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        ); }
129

130 131 132
    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; }
133 134 135 136

    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; }
137

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
138
    bool _isOk() const noexcept { return settings; }
139
    void reportError(QIdentifierLiteral error, QString context = QString()) { if (errorHandler) errorHandler(error, QString(currentPath().latin1()).append(context)); }
140 141
private:
    template<typename T>
142
    bool set(T& t, QIdentifierLiteral error) { QVariant v = settings->value(key()); if (v.convert(qMetaTypeId<T>())) { t = v.value<T>(); return true; } reportError(error); return false; }
143 144
    QString key() {
        Q_ASSERT(!levels.isEmpty());
145
        return !levels.last().key.isNull()
146 147
            ? QString        (levels.last().key.latin1())
            : QString::number(levels.last().idx         );
148 149 150
    }

    QSettings* settings;
151
    struct Level { QIdentifier key; int idx=-1; bool isGroup=true; Level(QIdentifier n=QIdentifier(), bool isGroup=true) : key(n), isGroup(isGroup) {} };
152 153 154
    QStack<Level> levels;
    bool isChoice = false;
};