...
 
Commits (2)
......@@ -112,8 +112,6 @@ public:
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool isValid() const noexcept { return io; }
bool trySequence(quint32* rows=nullptr) { levels.push_back(rows);
if (Q_LIKELY(rows)) { putInteger (*rows , cbor::ArrayType); }
else { putMajorValue(cbor::IndefiniteLength, cbor::ArrayType); } return true; }
......@@ -235,7 +233,7 @@ public:
QCborVisitor(QCborValue* v) : cbor(v) { Q_ASSERT(v); }
void reset(QCborValue* v) { cbor=v; Q_ASSERT(v); steps.resize(0); }
QVariant context() const {
QVariant valuePath() const {
QByteArray path;
Q_FOREACH(Step s, steps) {
if (!s.key.isNull()) { path.append('{').append( s.key.utf8() ); }
......@@ -267,7 +265,6 @@ protected:
bool tryOut ( ) { steps.pop() ; return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return cbor; }
private:
const QCborValue& current(int outer=0) const { Q_ASSERT(0<=outer); return steps.size()-outer <= 0 ? *cbor : steps[steps.size()-outer-1].item; }
......@@ -418,8 +415,7 @@ protected:
while (hasNext()) { tryAny(); }
return leaveContainer(); }
bool isValid() const noexcept { return const_cast<QCborReader*>(this)->lastError()==QCborError::NoError; }
QVariant context() const { return currentOffset(); }
QVariant valuePath() const { return currentOffset(); }
private:
void skipTag() { if (isTag()) next(); }
bool cacheOut() { if (!cacheLevel) {
......
......@@ -68,28 +68,26 @@ public:
template<typename T> QValueEnd zap(T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
friend class QCur;
bool trySequence(quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryRecord (quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryNull ( ) { *io << nullptr; return true; }
bool trySequence(quint32* s=nullptr) { if (s) *io << *s; return io->status()==QDataStream::Ok; }
bool tryRecord (quint32* s=nullptr) { if (s) *io << *s; return io->status()==QDataStream::Ok; }
bool tryNull ( ) { *io << nullptr; return io->status()==QDataStream::Ok; }
bool tryBind ( QUtf8DataView u) { QByteArray ba = QByteArray::fromRawData(u.data(), u.size()+int(sizeof('\0'))); return static_cast<QAbstractValue*>(this)->tryBind(ba); }
bool tryBind ( QUtf8Data&& t) { *io << t.utf8(); return true; }
bool tryBind ( QString&& t) { *io << t; return true; }
bool tryBind ( bool&& t) { *io << t; return true; }
bool tryBind ( qint8&& t) { *io << t; return true; }
bool tryBind ( quint8&& t) { *io << t; return true; }
bool tryBind ( qint16&& t) { *io << t; return true; }
bool tryBind ( quint16&& t) { *io << t; return true; }
bool tryBind ( qint32&& t) { *io << t; return true; }
bool tryBind ( quint32&& t) { *io << t; return true; }
bool tryBind ( qint64&& t) { *io << t; return true; }
bool tryBind ( quint64&& t) { *io << t; return true; }
bool tryBind ( float&& t) { *io << t; return true; }
bool tryBind ( double&& t) { *io << t; return true; }
bool tryBind ( QByteArray&& t) { *io << t; return true; }
bool tryBind ( QVariant&& t) { *io << t; return true; }
bool isValid() const noexcept { return io && io->status()==QDataStream::Ok; }
bool tryBind ( QUtf8Data&& t) { *io << t.utf8(); return io->status()==QDataStream::Ok; }
bool tryBind ( QString&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( bool&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( qint8&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( quint8&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( qint16&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( quint16&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( qint32&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( quint32&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( qint64&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( quint64&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( float&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( double&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( QByteArray&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryBind ( QVariant&& t) { *io << t; return io->status()==QDataStream::Ok; }
bool tryOut ( ) { return true; }
bool tryItem( ) { return true; }
......
......@@ -105,7 +105,7 @@ public:
QJsonVisitor(const QJsonValue* v) : json(v) { Q_ASSERT(v); }
void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); }
QVariant context() const {
QVariant valuePath() const {
QByteArray path;
Q_FOREACH(Step s, steps) {
if (!s.key.isNull()) { path.append('{').append( s.key.utf8() ); }
......@@ -136,7 +136,6 @@ protected:
bool tryOut ( ) { steps.removeLast(); return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return true; }
private:
const QJsonValue& current(int outer=0) const { Q_ASSERT(0<=outer && json); return steps.size()-outer <= 0 ? *json : steps[steps.size()-outer-1].item; }
......@@ -391,8 +390,7 @@ protected:
level.index++;
return true; }
bool isValid() const noexcept { return io; }
QVariant context() const { return QVariantList{line,column}; }
QVariant valuePath() const { return QVariantList{line,column}; }
private:
CachedNumber getNumber() {
if (cachedNumber!=None) return cachedNumber;
......
......@@ -100,6 +100,17 @@ protected:
}
}
static QVariantList path(QModelIndex current) {
if (current.isValid()) {
auto p = path(current.parent());
p.append(QVariantList{current.row(),current.column()});
return p;
}
return QVariantList();
}
QVariant valuePath() const { return path(parent); }
virtual QAbstractValue* itemBind() = 0;
//! Checks whether current dimension \c d is R or C, inverting R and C dimensions if !rowFirst
......@@ -165,7 +176,6 @@ public:
QModelWriter(QAbstractItemModel* m, bool rowFirst=true) : QModelBind(m, rowFirst), w(&ba) {}
virtual QValueMode mode() const noexcept { return QValueMode::Write; }
virtual bool isValid() const noexcept { return true; } //!< Write status ignored by default (no need to test intermediate status to choose between trySequence(), etc.
virtual QValue value() { return QCur(this).value(); }
protected:
virtual void _meta(QIdentifierLiteral& n, QAsciiData& meta) { return QModelBind::_meta(n, meta); }
......@@ -358,7 +368,6 @@ public:
QModelReader(QAbstractItemModel* m, bool rowFirst=true) : QModelBind(m, rowFirst), r(&io) { io.open(QIODevice::ReadOnly); }
virtual QValueMode mode() const noexcept { return QValueMode::Read; }
virtual bool isValid() const noexcept { return true; } //!< Read status ignored by default (QAbstractItemModel is not sequential)
virtual QValue value() { return QCur(this).value(); }
protected:
virtual void _meta(QIdentifierLiteral& n, QAsciiData& meta) { return QModelBind::_meta(n, meta); }
......@@ -561,16 +570,6 @@ protected:
QAbstractValue* itemBind() { return static_cast<QAbstractValue*>(&r); }
void setItemData(QUtf8Data u) { io.buffer() = u.utf8(); io.seek(0); r.reset(&io); }
QVariantList path(QModelIndex current) const {
if (current.isValid()) {
auto p = path(current.parent());
p.append(QVariantList{current.row(),current.column()});
return p;
}
return QVariantList();
}
QVariant context() const { return path(parent); }
QBuffer io;
TItemReader r;
};
......@@ -62,7 +62,7 @@ class QSettingsWriter : public QAbstractValueWriter
public:
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); }
QVariant context() const { return key; }
QVariant valuePath() const { return key; }
// Shortcuts
/**/ QValue value ( ) { return QCur(this).value(); }
......@@ -107,7 +107,7 @@ class QSettingsReader : public QAbstractValueReader
public:
QSettingsReader(QSettings* s) : settings(s) { Q_ASSERT(s); allKeys=settings->allKeys(); }
QVariant context() const { return key; }
QVariant valuePath() const { return key; }
// Shortcuts
template<typename T> QValueEnd bind(T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
......@@ -143,7 +143,6 @@ protected:
bool tryOut() { Q_UNUSED(pop()) key.chop(1); return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return settings; }
private:
template<typename T>
bool set(T& t) { QVariant v = settings->value(key); if (v.convert(qMetaTypeId<T>())) { t = v.value<T>(); return true; } return false; }
......
......@@ -2,7 +2,7 @@
QIdentifierLiteral qBindUnexpectedValue ("UnexpectedValue" );
QIdentifierLiteral qBindUnexpectedEnd ("UnexpectedEnd" );
QIdentifierLiteral qBindStopped ("Stopped" );
QIdentifierLiteral qBindIllFormedValue ("IllFormedValue" );
QIdentifierLiteral qBindExpectedItem ("ExpectedItem" );
QIdentifierLiteral qBindExpectedNull ("ExpectedNull" );
......
......@@ -54,7 +54,7 @@
extern QIdentifierLiteral qBindUnexpectedValue; //!< Values that cannot be bound (invalid or not supported by the bound data type or our data model)
extern QIdentifierLiteral qBindUnexpectedEnd;
extern QIdentifierLiteral qBindStopped;
extern QIdentifierLiteral qBindIllFormedValue;
extern QIdentifierLiteral qBindExpectedItem;
extern QIdentifierLiteral qBindExpectedNull;
......@@ -119,13 +119,18 @@ using QValueErrorFilter = std::function<bool(QIdentifierLiteral,QVariant)>;
//#include <QtCore/quuid.h>
//! Interface for QValue implementations with a fixed subset of BindNative types and just a few optional methods
//!
//! Every tryXxx method must either:
//! \li return true if anything was done with the data that cannot be reversed (such as Read or Write Xxx construct, even only partially)
//! \li else return false, allowing other tryXxx to be called from the same place
//!
//! \warning tryXxxx methods MUST call setIllFormed() whenever they started doing things with the data that can be neither completed nor reversed
//!
struct QAbstractValue {
virtual ~QAbstractValue() = default;
virtual QValueMode mode() const noexcept = 0; //!< \remark a static constexpr QValueMode Mode did not exhibit noticeable performance improvements and may trigger twice more code generation for Read/Write independant QTransmogrifier like Person::bind
virtual bool isValid() const noexcept = 0; //!< Current operation status
virtual bool trySequence(quint32* size=nullptr) = 0;
virtual bool tryRecord (quint32* size=nullptr) = 0;
......@@ -201,16 +206,18 @@ struct QAbstractValue {
//! \warning meta() is ignored by default and subject to various interpretation, so users should define or adopt existing meta data standards like XSD for sake of interoperability
virtual void _meta(QIdentifierLiteral&, QAsciiData&) {}
virtual void stop() { Q_VERIFY(isErrorFiltered(qBindStopped)) }
virtual void setIllFormed() { illFormed = true; Q_VERIFY(isErrorFiltered(qBindIllFormedValue)) }
virtual bool isIllFormed() const noexcept { return illFormed; }; //!< Current operation status
virtual QValueErrorFilter setErrorFilter(QValueErrorFilter newFilter) {
auto previousHandler = errorFilter;
errorFilter = newFilter;
return previousHandler;
}
virtual bool isErrorFiltered(QIdentifierLiteral e, QVariant context = QVariant()) const { auto c = this->context(); return errorFilter && errorFilter(e, c.isNull() ? context : context.isNull() ? c : QVariantList({context, this->context()})); }
virtual QVariant context() const { return QVariant(); }
virtual bool isErrorFiltered(QIdentifierLiteral e, QVariant context = QVariant()) const { auto c = this->valuePath(); return errorFilter && errorFilter(e, c.isNull() ? context : context.isNull() ? c : QVariantList({context, this->valuePath()})); }
virtual QVariant valuePath() const { return QVariant(); }
protected:
bool illFormed = false;
QValueErrorFilter errorFilter = nullptr;
};
......@@ -247,7 +254,7 @@ public:
QValueMode mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
explicit operator bool() const noexcept { return isValid(); }
bool isValid() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
bool isValid() const noexcept { return impl && !impl->isIllFormed(); } //!< Drives QTransmogrifier<T>::bind() traversal
QCur* operator->() noexcept { return this; }
QVal<QCur> value() noexcept ;
......@@ -337,7 +344,7 @@ private:
template<class T> friend class QVal;
template<class T> friend class QSeq;
template<class T> friend class QRec;
void stop() { if (Q_LIKELY(impl)) { impl->stop(); impl = nullptr; } }
void setIllFormed() { if (Q_LIKELY(impl)) { impl->setIllFormed(); impl = nullptr; } }
QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); } // FIXME replace with std::move(outer) to avoid resuming read/write when QCur was actually stopped!
......@@ -408,7 +415,7 @@ public:
resumeErrorHandler() &&
outer->isErrorFiltered(qBindUnexpectedValue) &&
!outer->tryAny()) {
outer->stop(); // do not attempt outer.out()
outer->setIllFormed(); // do not attempt outer.out()
}
}
explicit QVal(T_&& out) noexcept { outer = std::move(out); }
......@@ -491,7 +498,7 @@ public:
if (isValid() &&
outer->isErrorFiltered(qBindUnexpectedEnd) &&
!outer->trySequenceOut()) {
outer->stop();
outer->setIllFormed();
}
}
......@@ -545,7 +552,7 @@ public:
if (isValid() &&
outer->isErrorFiltered(qBindUnexpectedEnd) &&
!outer->tryRecordOut()) {
outer->stop();
outer->setIllFormed();
}
}
......@@ -639,8 +646,6 @@ struct QAbstractValueWriter : public QAbstractValue
virtual QValueMode mode() const noexcept { return QValueMode::Write; }
virtual bool isValid() const noexcept { return true; } //!< Write status ignored by default (no need to test intermediate status to choose between trySequence(), etc.
virtual bool tryItem( ) = 0;
virtual bool tryItem(QIdentifier& n) = 0;
virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id(n); return tryItem(id); }
......@@ -823,8 +828,8 @@ struct QAbstractValueReader : public QAbstractValue
{
QSuspendedValueErrorHandler(this);
quint32 size=0; QIdentifier key; QVariant item;
if (trySequence(&size)) { QVariantList l; while (tryItem( )) { if (!tryBind(item)) { item = QVariant(); if (!(isErrorFiltered(qBindUnexpectedValue) && tryAny())) { stop(); return true; } } l.append( item); } dst = l; return trySequenceOut(); }
if (tryRecord (&size)) { QVariantMap l; while (tryItem(key)) { if (!tryBind(item)) { item = QVariant(); if (!(isErrorFiltered(qBindUnexpectedValue) && tryAny())) { stop(); return true; } } l.insert(key.latin1(), item); } dst = l; return tryRecordOut (); }
if (trySequence(&size)) { QVariantList l; while (tryItem( )) { if (!tryBind(item)) { item = QVariant(); if (!(isErrorFiltered(qBindUnexpectedValue) && tryAny())) { setIllFormed(); return true; } } l.append( item); } dst = l; return trySequenceOut(); }
if (tryRecord (&size)) { QVariantMap l; while (tryItem(key)) { if (!tryBind(item)) { item = QVariant(); if (!(isErrorFiltered(qBindUnexpectedValue) && tryAny())) { setIllFormed(); return true; } } l.insert(key.latin1(), item); } dst = l; return tryRecordOut (); }
bool b; if (tryBind( b)) { dst = QVariant(b); return true; }
quint64 u; if (tryBind( u)) { dst = QVariant(u); return true; }
qint64 l; if (tryBind( l)) { dst = QVariant(l); return true; }
......@@ -1144,7 +1149,7 @@ struct QTransmogrifier<QVector<T>> {
for (auto&& t : ts) {
s = s.bind(t);
}
return s;
return s.out();
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
......
......@@ -122,7 +122,7 @@ public:
QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); }
void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); }
QVariant context() const {
QVariant valuePath() const {
QByteArray path;
Q_FOREACH(Level l, levels) {
if (l.key.isNull()) { path.append('{').append( l.key.utf8() ); }
......@@ -176,7 +176,6 @@ protected:
bool tryOut ( ) { levels.pop() ; return true; }
bool tryAny () { return true; }
bool isValid() const noexcept { return value; }
private:
const QVariant* current(unsigned outer=0) const { return unsigned(levels.size())-outer <= 0 ? value : &(levels[unsigned(levels.size())-outer-1].item); }
......
......@@ -72,23 +72,21 @@ public:
template<typename T> QValueEnd bind(T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* =nullptr) { io->writeStartElement(tag("sequence" ).latin1()); att(); return true; }
bool tryRecord (quint32* =nullptr) { io->writeStartElement(tag("record" ).latin1()); att(); return true; }
bool tryAny ( ) { io->writeEmptyElement(tag("undefined").latin1()); att(); return true; }
bool tryNull ( ) { io->writeEmptyElement(tag("null" ).latin1()); att(); return true; }
bool tryBind ( QUtf8DataView u) { writeText("string" ,QString::fromUtf8(u.data(), u.size())); return true; }
bool tryBind ( QStringView s) { writeText("string" , s.toString() ); return true; }
bool tryBind ( QString&& s) { writeText("string" , s ); return true; }
bool tryBind ( bool&& b) { writeText("boolean", b ? QStringLiteral("true") : QStringLiteral("false")); return true; }
bool tryBind ( qint64&& n) { static QString s; s.setNum( n ); writeText("integer" , s); return true; }
bool tryBind ( quint64&& n) { static QString s; s.setNum( n ); writeText("integer" , s); return true; }
bool tryBind ( float&& n) { static QString s; s.setNum(double(n),'g',std::numeric_limits< float>::max_digits10); writeText("decimal" , s); return true; } // with specific precision
bool tryBind ( double&& n) { static QString s; s.setNum( n ,'g',std::numeric_limits<double>::max_digits10); writeText("decimal" , s); return true; } // with specific precision
bool tryBind ( QByteArray&& s) { QString h; h.reserve(s.size()*2+2+1); h.append("0x").append(s.toHex().toUpper()) ; writeText("hexBinary", h); return true; }
bool trySequence(quint32* =nullptr) { io->writeStartElement(tag("sequence" ).latin1()); att(); return !io->hasError(); }
bool tryRecord (quint32* =nullptr) { io->writeStartElement(tag("record" ).latin1()); att(); return !io->hasError(); }
bool tryAny ( ) { io->writeEmptyElement(tag("undefined").latin1()); att(); return !io->hasError(); }
bool tryNull ( ) { io->writeEmptyElement(tag("null" ).latin1()); att(); return !io->hasError(); }
bool tryBind ( QUtf8DataView u) { return writeText("string" ,QString::fromUtf8(u.data(), u.size()) ); }
bool tryBind ( QStringView s) { return writeText("string" , s.toString() ); }
bool tryBind ( QString&& s) { return writeText("string" , s ); }
bool tryBind ( bool&& b) { return writeText("boolean", b ? QStringLiteral("true") : QStringLiteral("false")); }
bool tryBind ( qint64&& n) { static QString s; s.setNum( n ); return writeText("integer" , s); }
bool tryBind ( quint64&& n) { static QString s; s.setNum( n ); return writeText("integer" , s); }
bool tryBind ( float&& n) { static QString s; s.setNum(double(n),'g',std::numeric_limits< float>::max_digits10); return writeText("decimal" , s); } // with specific precision
bool tryBind ( double&& n) { static QString s; s.setNum( n ,'g',std::numeric_limits<double>::max_digits10); return writeText("decimal" , s); } // with specific precision
bool tryBind ( QByteArray&& s) { QString h; h.reserve(s.size()*2+2+1); h.append("0x").append(s.toHex().toUpper()) ; return writeText("hexBinary", h); }
bool isValid() const noexcept { return !io->hasError() || isErrorFiltered(qBindUnexpectedEnd, io->device()->errorString()); }
bool tryOut () { io->writeEndElement(); return true; }
bool tryOut () { io->writeEndElement(); return !io->hasError(); }
bool tryItem() { return true; }
bool tryItem(QIdentifier& n) { name=n; return true; }
void _meta(QIdentifierLiteral &n, QAsciiData &m);
......@@ -101,7 +99,7 @@ private:
QAsciiData& operator[](QIdentifierLiteral n) { return find(n)->second; }
};
void writeText(const char* def, const QString& text) {
bool writeText(const char* def, const QString& text) {
io->writeStartElement(tag(def).latin1());
att();
QString valid;
......@@ -111,6 +109,7 @@ private:
}
io->writeCharacters(valid);
io->writeEndElement();
return !io->hasError();
}
QIdentifier tag(const char* def) { if (name.isNull()) { return QIdentifier(def); } auto n=name; name=QIdentifier(); return n; }
void att() { for (auto&& a : atts) { if (!a.second.isNull()) io->writeAttribute(a.first.latin1(), a.second.latin1()); } }
......